/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to provide the required interfact for the cagd library for the    *
* free form surfaces and curves.					     *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include "program.h"
#include "allocate.h"
#include "attribut.h"
#include "objects.h"
#include "primitiv.h"
#include "windows.h"
#include "ip_cnvrt.h"
#include "freeform.h"

static IPObjectStruct *GetControlMesh(IPObjectStruct *LstObjList,
				      int UOrder,
				      int VOrder,
				      CagdGeomType GType,
				      char **ErrStr);
static IPObjectStruct *GetControlPoly(IPObjectStruct *PtObjList,
				      int Order,
				      CagdGeomType GType,
				      char **ErrStr);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to fetch the boolean object DrawCtlPt.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        Value of DrawCtlPt object.                                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetDrawCtlPt                                                             M
*****************************************************************************/
int GetDrawCtlPt(void)
{
    int DrawCtlPt;
    IPObjectStruct
	*PObj = GetObject("DRAWCTLPT");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	IritPrsrFatalError("No numeric object name DRAWCTLPT is defined");
	DrawCtlPt = 0;
    }
    else
	DrawCtlPt = REAL_TO_INT(PObj -> U.R);

    return DrawCtlPt;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to fetch the boolean object FourPerFlat.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        Value of Flat4Ply object.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetFourPerFlat                                                           M
*****************************************************************************/
int GetFourPerFlat(void)
{
    int FourPerFlat;
    IPObjectStruct
	*PObj = GetObject("FLAT4PLY");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	IritPrsrFatalError("No numeric object name FLAT4PLY is defined");
	FourPerFlat = 0;
    }
    else
	FourPerFlat = REAL_TO_INT(PObj -> U.R);

    return FourPerFlat;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to fetch the boolean object Poly_Approx_opt.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        Value of Poly_Approx_opt object.                             M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetPolyApproxOptimal                                                     M
*****************************************************************************/
int GetPolyApproxOptimal(void)
{
    int PolyApproxOpt;
    IPObjectStruct
	*PObj = GetObject("POLY_APPROX_OPT");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	IritPrsrFatalError("No numeric object name POLY_APPROX_OPT is defined");
	PolyApproxOpt = 0;
    }
    else
	PolyApproxOpt = REAL_TO_INT(PObj -> U.R);

    return PolyApproxOpt;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to fetch the boolean object Poly_Approx_Uv.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        Value of Poly_Approx_Uv object.                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetPolyApproxUV                                                          M
*****************************************************************************/
int GetPolyApproxUV(void)
{
    int PolyApproxUV;
    IPObjectStruct
	*PObj = GetObject("POLY_APPROX_UV");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	IritPrsrFatalError("No numeric object name POLY_APPROX_UV is defined");
	PolyApproxUV = 0;
    }
    else
	PolyApproxUV = REAL_TO_INT(PObj -> U.R);

    return PolyApproxUV;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to fetch the boolean object Poly_Approx_Tol.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        Value of Poly_Approx_Tol object.                             M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetPolyApproxTol                                                         M
*****************************************************************************/
RealType GetPolyApproxTol(void)
{
    RealType PolyApproxTol;
    IPObjectStruct
	*PObj = GetObject("POLY_APPROX_TOL");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	IritPrsrFatalError("No numeric object name POLY_APPROX_TOL is defined");
	PolyApproxTol = 0.1;
    }
    else
	PolyApproxTol = PObj -> U.R;

    return PolyApproxTol;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to copy the control mesh lists to a surface control mesh.          *
*   The surface is allocated here as well.				     *
*   Returns the surface if o.k., otherwise NULL.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   LstObjList: A list object of lists of control points.                    *
*   UOrder:     U order of surface.                                          *
*   VOrder:     V order of surface.                                          *
*   GType:      Geometry type - Bezier, Bspline etc.                         *
*   ErrStr:     If an error, detected, this is initialized with description. *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:   A surface object if successful, NULL otherwise.      *
*****************************************************************************/
static IPObjectStruct *GetControlMesh(IPObjectStruct *LstObjList,
				      int UOrder,
				      int VOrder,
				      CagdGeomType GType,
				      char **ErrStr)
{
    int i, j, k, PtSize,
	NumVertices = 0,
	NumVerticesFirst = -1,
        NumLists = 0;
    CagdRType **r;
    RealType *v;
    IPObjectStruct *SrfObj, *LstObj, *PtObj;
    CagdPointType
	PtType = CAGD_PT_E1_TYPE;

    if (!IP_IS_OLST_OBJ(LstObjList))
	IritFatalError("SURFACE: Not object list object!");

    while ((LstObj = ListObjectGet(LstObjList, NumLists)) != NULL) {
	if (!IP_IS_OLST_OBJ(LstObj)) {
	    *ErrStr = "Non list object found in list";
	    return NULL;
	}

        NumVertices = -1;
        while ((PtObj = ListObjectGet(LstObj, ++NumVertices)) != NULL) {
	    if (!IP_IS_CTLPT_OBJ(PtObj) &&
		!IP_IS_POINT_OBJ(PtObj) &&
		!IP_IS_VEC_OBJ(PtObj)) {
		*ErrStr = "Non point object found in list";
		return NULL;
	    }
	}
	if ((PtType = IritPrsrCoerceCommonSpace(LstObj, PtType)) ==
								CAGD_PT_NONE) {
	    *ErrStr = "";
	    return NULL;
	}

	if (NumLists++ == 0)
	    NumVerticesFirst = NumVertices;
        else if (NumVerticesFirst != NumVertices) {
	    *ErrStr = "Different size of point lists";
	    return NULL;
	}
    }

    /* Coerce all points to a common space, in place. */
    while ((LstObj = ListObjectGet(LstObjList, NumLists)) != NULL)
	if (IritPrsrCoercePtsListTo(LstObj, PtType) == CAGD_PT_NONE) {
	    *ErrStr = "";
	    return NULL;
	}

    if (NumVertices < 2 || NumLists < 2) {
	*ErrStr = "Less than 2 points in a row/col";
	return NULL;
    }

    SrfObj = GenSRFObject(NULL);
    switch (GType) {
	case CAGD_SBEZIER_TYPE:
	    SrfObj -> U.Srfs = BzrSrfNew(NumVertices, NumLists, PtType);
	    break;
	case CAGD_SBSPLINE_TYPE:
	    SrfObj -> U.Srfs = BspSrfNew(NumVertices, NumLists,
					 UOrder, VOrder, PtType);
	    break;
	default:
	    break;
    }
    AttrSetObjectColor(SrfObj, GlblPrimColor);	   /* Set its default color. */
    PtSize = CAGD_IS_RATIONAL_PT(PtType) + CAGD_NUM_OF_PT_COORD(PtType);

    for (r = SrfObj -> U.Srfs -> Points, i = 0; i < NumLists; i++) {
	LstObj = ListObjectGet(LstObjList, i);

        for (j = 0; j < NumVertices; j++) {
	    IPObjectStruct
		*VObj = ListObjectGet(LstObj, j);

	    v = VObj -> U.CtlPt.Coords;

	    if (CAGD_IS_RATIONAL_PT(PtType))
		for (k = 0; k < PtSize; k++)
		    r[k][i * NumVertices + j] = *v++;
	    else
		for (k = 1; k <= PtSize; k++)
		    r[k][i * NumVertices + j] = *++v;
        }
    }

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to copy the control polygon to a curve's control polygon.          *
*   The curve is allocated here as well.				     *
*   Returns the surface if o.k., otherwise NULL.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   PtObjList:  A list object of control points.   	  	             *
*   Order:      Order of curve.	                                	     *
*   GType:      Geometry type - Bezier, Bspline etc.                         *
*   ErrStr:     If an error, detected, this is initialized with description. *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:   A curve object if successful, NULL otherwise.        *
*****************************************************************************/
static IPObjectStruct *GetControlPoly(IPObjectStruct *PtObjList,
				      int Order,
				      CagdGeomType GType,
				      char **ErrStr)
{
    int i, j, PtSize,
        NumVertices = -1;
    CagdRType **r;
    RealType *v;
    IPObjectStruct *CrvObj, *PtObj;
    CagdPointType PtType;

    *ErrStr = NULL;

    if (!IP_IS_OLST_OBJ(PtObjList))
	IritFatalError("CURVE: Not object list object!");

    while ((PtObj = ListObjectGet(PtObjList, ++NumVertices)) != NULL) {
	if (!IP_IS_CTLPT_OBJ(PtObj) &&
	    !IP_IS_POINT_OBJ(PtObj) &&
	    !IP_IS_VEC_OBJ(PtObj)) {
	    *ErrStr = "Non point object found in list";
	    return NULL;
	}
    }
	
    if (NumVertices < 2) {
	*ErrStr = "Less than 2 points";
	return NULL;
    }

    /* Coerce all points to a common space, in place. */
    if ((PtType = IritPrsrCoercePtsListTo(PtObjList, CAGD_PT_E1_TYPE))
							== CAGD_PT_NONE) {
	*ErrStr = "";
	return NULL;
    }

    CrvObj = IPAllocObject("", IP_OBJ_CURVE, NULL);
    switch (GType) {
	case CAGD_CBEZIER_TYPE:
	    CrvObj -> U.Crvs = BzrCrvNew(NumVertices, PtType);
	    break;
	case CAGD_CBSPLINE_TYPE:
	    CrvObj -> U.Crvs = BspCrvNew(NumVertices, Order, PtType);
	    break;
	default:
	    break;
    }
    AttrSetObjectColor(CrvObj, GlblPrimColor);	   /* Set its default color. */
    PtSize = CAGD_IS_RATIONAL_PT(PtType) + CAGD_NUM_OF_PT_COORD(PtType);

    for (r = CrvObj -> U.Crvs -> Points, i = 0; i < NumVertices; i++) {
	IPObjectStruct
	    *VObj = ListObjectGet(PtObjList, i);

	v = VObj -> U.CtlPt.Coords;

	if (CAGD_IS_RATIONAL_PT(PtType))
	    for (j = 0; j < PtSize; j++)
		r[j][i] = *v++;
	else
	    for (j = 1; j <= PtSize; j++)
		r[j][i] = *++v;
    }

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to copy the list of knots into the knot vector provided.           *
*   Returns KnotVector if o.k., NULL otherwise (sets ErrStr to description). *
*   Length can hold the requested length (in ctlpts) in case a uniform KV is *
* requested. Length will return the actual length of the KV constructed.     *
*                                                                            *
* PARAMETERS:                                                                *
*   KntObjList: A list of knots (numeric values).                            *
*   Order:      Order of geometry to use this knot vector.                   *
*   Length:     Expected length of knot vector.                              *
*   ErrStr:     If an error, detected, this is initialized with description. *
*                                                                            *
* RETURN VALUE:                                                              *
*   CagdRType *:   Allocated knot vector, or NULL if error.                  *
*****************************************************************************/
CagdRType *GetKnotVector(IPObjectStruct *KntObjList,
			 int Order,
			 int *Length,
			 char **ErrStr)
{
    int NumKnots = 0,
	RequestLength = *Length;
    CagdRType *KnotVector;
    IPObjectStruct *KntObj;

    *ErrStr = NULL;

    if (!IP_IS_OLST_OBJ(KntObjList))
	IritFatalError("KNOT: Not object list object!");

    *Length = ListObjectLength(KntObjList);
    KnotVector = (CagdRType *) IritMalloc(sizeof(CagdRType) * *Length);

    while ((KntObj = ListObjectGet(KntObjList, NumKnots)) != NULL &&
	   NumKnots < *Length) {
	if (!IP_IS_NUM_OBJ(KntObj)) {
	    *ErrStr = "Non numeric object found in list";
	    return NULL;
	}

	KnotVector[NumKnots++] = KntObj -> U.R;
    }

    if (NumKnots == 1 && KnotVector[0] < KV_MIN_LEGAL) {
	int KVType = REAL_TO_INT(KnotVector[0]);

	IritFree((VoidPtr) KnotVector);

	*Length = RequestLength + Order;

	switch (KVType) {
	    case KV_UNIFORM_OPEN:
		KnotVector = BspKnotUniformOpen(RequestLength, Order, NULL);
		break;
	    case KV_UNIFORM_FLOAT:
		KnotVector = BspKnotUniformFloat(RequestLength, Order, NULL);
		break;
	    case KV_UNIFORM_PERIODIC:
		KnotVector = BspKnotUniformPeriodic(RequestLength + Order - 1,
						    Order, NULL);
		*Length += Order - 1;
		break;
	    default:
		*ErrStr = "Invalid knot value";
		fprintf(stderr,"Knot = %10.6g (%d)", KnotVector[0], KVType);
		return NULL;
	}
    }
    else if (NumKnots != *Length) {
	*ErrStr = "Wrong knot vector length";
	return NULL;
    }

    return KnotVector;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to create a Bezier surface geometric object defined by a list of M
* lists of vertices.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   LstObjList: A list object of lists of control points.                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A Bezier surface object if successful, NULL otherwise. M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBezierSurfaceObject                                                   M
*****************************************************************************/
IPObjectStruct *GenBezierSurfaceObject(IPObjectStruct *LstObjList)
{
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*SrfObj = GetControlMesh(LstObjList, -1, -1, CAGD_SBEZIER_TYPE, &ErrStr);

    if (SrfObj == NULL) {
	sprintf(Line, "SBEZIER: %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
    }

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to create a Bezier curve geometric object defined by a list of   M
* vertices.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   PtObjList:  A list object of control points.   	  	             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A Bezier curve object if successful, NULL otherwise.   M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBezierCurveObject                                                     M
*****************************************************************************/
IPObjectStruct *GenBezierCurveObject(IPObjectStruct *PtObjList)
{
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*CrvObj = GetControlPoly(PtObjList, -1, CAGD_CBEZIER_TYPE, &ErrStr);

    if (CrvObj == NULL) {
	sprintf(Line, "CBEZIER: %s\n, empty object result.", ErrStr);
	IritPrsrFatalError(Line);
    }

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to create a Bspline surface geometric object defined by a list   M
* of lists of vertices.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   RUOrder:     U order of surface.                                         M
*   RVOrder:     V order of surface.                                         M
*   LstObjList:  A list object of lists of control points.                   M
*   KntObjList:  A list of knots (numeric values).                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A Bspline surface object if successful, NULL otherwise.M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBsplineSurfaceObject                                                  M
*****************************************************************************/
IPObjectStruct *GenBsplineSurfaceObject(RealType *RUOrder,
					RealType *RVOrder,
					IPObjectStruct *LstObjList,
					IPObjectStruct *KntObjList)
{
    int Len1, Len2,
	UOrder = REAL_PTR_TO_INT(RUOrder),
	VOrder = REAL_PTR_TO_INT(RVOrder);
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*SrfObj = GetControlMesh(LstObjList, UOrder, VOrder,
					  CAGD_SBSPLINE_TYPE, &ErrStr);

    if (SrfObj == NULL) {
	sprintf(Line, "SBSPLINE: Ctl mesh, %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
	return NULL;
    }

    if (!IP_IS_OLST_OBJ(KntObjList) || ListObjectLength(KntObjList) != 2) {
	IPFreeObject(SrfObj);
	IritPrsrFatalError("SBSPLINE: Exactly two knot vectors expected");
	return NULL;
    }

    if (SrfObj -> U.Srfs -> ULength < SrfObj -> U.Srfs -> UOrder ||
	SrfObj -> U.Srfs -> VLength < SrfObj -> U.Srfs -> VOrder) {
	IPFreeObject(SrfObj);
	IritPrsrFatalError("SBSPLINE: Surface mesh length smaller than order.");
	return NULL;
    }

    IritFree((VoidPtr) SrfObj -> U.Srfs -> UKnotVector);
    SrfObj -> U.Srfs -> UKnotVector = NULL;
    IritFree((VoidPtr) SrfObj -> U.Srfs -> VKnotVector);
    SrfObj -> U.Srfs -> VKnotVector = NULL;
    Len1 = SrfObj -> U.Srfs -> ULength;
    Len2 = SrfObj -> U.Srfs -> VLength;
    if ((SrfObj -> U.Srfs -> UKnotVector =
	 GetKnotVector(ListObjectGet(KntObjList, 0), UOrder,
		       &Len1, &ErrStr)) == NULL ||
	(SrfObj -> U.Srfs -> VKnotVector =
	 GetKnotVector(ListObjectGet(KntObjList, 1), VOrder,
		       &Len2, &ErrStr)) == NULL) {
	IPFreeObject(SrfObj);
	sprintf(Line, "SBSPLINE: Knot vectors, %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
	return NULL;
    }

    if (Len1 != SrfObj -> U.Srfs -> ULength + UOrder) {
	if (Len1 == SrfObj -> U.Srfs -> ULength + UOrder + UOrder - 1)
	    SrfObj -> U.Srfs -> UPeriodic = TRUE;
	else {
	    IPFreeObject(SrfObj);
	    IritPrsrFatalError("Wrong knot vector length");
	    return NULL;
	}
    }
    if (Len2 != SrfObj -> U.Srfs -> VLength + VOrder) {
	if (Len2 == SrfObj -> U.Srfs -> VLength + VOrder + VOrder - 1)
	    SrfObj -> U.Srfs -> VPeriodic = TRUE;
	else {
	    IPFreeObject(SrfObj);
	    IritPrsrFatalError("Wrong knot vector length");
	    return NULL;
	}
    }

    return SrfObj;

}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to create a Bspline curve geometric object defined by a list     M
* of vertices.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   ROrder:      Order of surface.                                           M
*   PtObjList:   A list object of control points.   	  	             M
*   KntObjList:  A list of knots (numeric values).                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A Bspline curve object if successful, NULL otherwise.  M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBsplineCurveObject                                                    M
*****************************************************************************/
IPObjectStruct *GenBsplineCurveObject(RealType *ROrder,
				      IPObjectStruct *PtObjList,
				      IPObjectStruct *KntObjList)
{
    int Len,
	Order = REAL_PTR_TO_INT(ROrder);
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*CrvObj = GetControlPoly(PtObjList, Order, CAGD_CBSPLINE_TYPE, &ErrStr);

    if (CrvObj == NULL) {
	sprintf(Line, "CBSPLINE: Ctl polygon, %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
	return NULL;
    }

    if (!IP_IS_OLST_OBJ(KntObjList)) {
	IPFreeObject( CrvObj);
	IritPrsrFatalError("CBSPLINE: Exactly one knot vector expected");
	return NULL;
    }

    if (CrvObj -> U.Crvs -> Length < CrvObj -> U.Crvs -> Order) {
	IPFreeObject(CrvObj);
	IritPrsrFatalError("CBSPLINE: Curve polygon length smaller than order.");
	return NULL;
    }

    IritFree((VoidPtr) CrvObj -> U.Crvs -> KnotVector);
    CrvObj -> U.Crvs -> KnotVector = NULL;
    Len = CrvObj -> U.Crvs -> Length;
    if ((CrvObj -> U.Crvs -> KnotVector =
	 GetKnotVector(KntObjList, Order, &Len, &ErrStr)) == NULL) {
	IPFreeObject(CrvObj);
	sprintf(Line, "CBSPLINE: Knot vector, %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
	return NULL;
    }

    if (Len != CrvObj -> U.Crvs -> Length + Order) {
	if (Len == CrvObj -> U.Crvs -> Length + Order + Order - 1)
	    CrvObj -> U.Crvs -> Periodic = TRUE;
	else {
	    IPFreeObject(CrvObj);
	    IritPrsrFatalError("CBSPLINE: Wrong knot vector length");
	    return NULL;
	}
    }

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to approximate a surface of revolution by rotating the given       M
* cross section along Z axes, as a cubic POLYNOMIAL surface.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Cross:    A curve torotate around the Zaxis.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A surface of revolution by rotating Cross around Z.   M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenSURFPREVObject                                                        M
*****************************************************************************/
IPObjectStruct *GenSURFPREVObject(IPObjectStruct *Cross)
{
    if (IP_IS_CRV_OBJ(Cross)) {
	if (CAGD_NUM_OF_PT_COORD(Cross -> U.Crvs -> PType) < 3) {
	    IritPrsrFatalError("SurfRev: cross-section perpendicular to Z. Empty object result");
	    return NULL;
	}

	return GenSRFObject(CagdSurfaceRevPolynomialApprox(Cross -> U.Crvs));
    }
    else
        return NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to subdivide a surface into two in specified direction (1 or 2)    M
* and specified parameter value.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:       Surface to subdivide.                                      M
*   RDir:         Direction of subdivision. Either U or V.                   M
*   ParamVal:     Parameter value at which subdivision should occur.         M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object of two surface objects, result of the   M
*		       subdivision.					     M
*                                                                            *
* KEYWORDS:                                                                  M
*   DivideSurfaceObject                                                      M
*****************************************************************************/
IPObjectStruct *DivideSurfaceObject(IPObjectStruct *SrfObj,
				    RealType *RDir,
				    RealType *ParamVal)
{
    int Dir = REAL_PTR_TO_INT(RDir);
    CagdSrfStruct
	*Srf = CagdSrfSubdivAtParam(SrfObj -> U.Srfs, *ParamVal, Dir);
    IPObjectStruct *Srf1, *Srf2, *SrfList;

    if (Srf == NULL)
	return NULL;

    Srf1 = GenSRFObject(Srf),
    AttrSetObjectColor(Srf1, AttrGetObjectColor(SrfObj));
    Srf2 = GenSRFObject(Srf -> Pnext),
    AttrSetObjectColor(Srf2, AttrGetObjectColor(SrfObj));
    Srf -> Pnext = NULL;

    SrfList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
    ListObjectInsert(SrfList, 0, Srf1);
    ListObjectInsert(SrfList, 1, Srf2);
    ListObjectInsert(SrfList, 2, NULL);

    return SrfList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to extract a surface region in specified direction (1 or 2) and    M
* specified parameter values.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:      Surface to extract a region from.                           M
*   RDir:        Direction of region extraction. Either U or V.              M
*   ParamVal1:   Parameter of beginning of region.                           M
*   ParamVal2:   Parameter of end of region.                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A region of SrfObj,                                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   RegionFromSurfaceObject                                                  M
*****************************************************************************/
IPObjectStruct *RegionFromSurfaceObject(IPObjectStruct *SrfObj, 
					RealType *RDir,
					RealType *ParamVal1,
					RealType *ParamVal2)
{
    int Dir = REAL_PTR_TO_INT(RDir);
    CagdSrfStruct
	*Srf = CagdSrfRegionFromSrf(SrfObj -> U.Srfs,
				    *ParamVal1, *ParamVal2, Dir);

    if (Srf == NULL)
	return NULL;

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to subdivide a curve into two in specified parameter value.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:       Curve to subdivide.                                        M
*   ParamVal:     Parameter value at which subdivision should occur.         M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object of two curve objects, result of the     M
*		       subdivision.					     M
*                                                                            *
* KEYWORDS:                                                                  M
*   DivideCurveObject                                                        M
*****************************************************************************/
IPObjectStruct *DivideCurveObject(IPObjectStruct *CrvObj, RealType *ParamVal)
{
    CagdCrvStruct
	*Crv = CagdCrvSubdivAtParam(CrvObj -> U.Crvs, *ParamVal);
    IPObjectStruct *Crv1, *Crv2, *CrvList;

    if (Crv == NULL)
	return NULL;

    Crv1 = GenCRVObject(Crv);
    AttrSetObjectColor(Crv1, AttrGetObjectColor(CrvObj));
    Crv2 = GenCRVObject(Crv -> Pnext);
    AttrSetObjectColor(Crv2, AttrGetObjectColor(CrvObj));
    Crv -> Pnext = NULL;

    CrvList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
    ListObjectInsert(CrvList, 0, Crv1);
    ListObjectInsert(CrvList, 1, Crv2);
    ListObjectInsert(CrvList, 2, NULL);

    return CrvList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to extract a curve region in specified parameter values.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:      Curve to extract a region from.                             M
*   ParamVal1:   Parameter of beginning of region.                           M
*   ParamVal2:   Parameter of end of region.                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A region of CrvObj,                                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   RegionFromCurveObject                                                    M
*****************************************************************************/
IPObjectStruct *RegionFromCurveObject(IPObjectStruct *CrvObj,
				      RealType *ParamVal1,
				      RealType *ParamVal2)
{
    CagdCrvStruct
	*Crv = CagdCrvRegionFromCrv(CrvObj -> U.Crvs, *ParamVal1, *ParamVal2);

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to refine a surface in specified direction (1 or 2) and knot       M
* vector.								     M
*   If, however, Replace is non zero, KnotsObj REPLACES current vector.      M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:     Surface to refine in direction RDir.                         M
*   RDir:       Direction of refinement. Either U or V,                      M
*   RReplace:   If TRUE KnotsObj will replace the RDir knot vector of SrfObj.M
*		Otherwise, the knots in KnotsObj will be added to it.	     M
*   KnotsObj:   A list of knots.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A refined surface, or a surface with a replaced knot M
*                       vector.                                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   RefineSurfaceObject                                                      M
*****************************************************************************/
IPObjectStruct *RefineSurfaceObject(IPObjectStruct *SrfObj,
				    RealType *RDir,
				    RealType *RReplace,
				    IPObjectStruct *KnotsObj)
{
    int n,
	Replace = REAL_PTR_TO_INT(RReplace),
	Dir = REAL_PTR_TO_INT(RDir);
    char *ErrStr, Line[LINE_LEN];
    CagdRType
	*t = GetKnotVector(KnotsObj, 0, &n, &ErrStr);
    CagdSrfStruct *RefSrf;
    IPObjectStruct *RefSrfObj;

    if (t == NULL) {
	IPFreeObject(SrfObj);
	sprintf(Line, "REFINE: %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
	return NULL;
    }
    RefSrf = CagdSrfRefineAtParams(SrfObj -> U.Srfs, Dir, Replace, t, n);
    IritFree((VoidPtr) t);
    if (RefSrf == NULL)
	return NULL;

    RefSrfObj = GenSRFObject(RefSrf),
    AttrSetObjectColor(RefSrfObj, AttrGetObjectColor(SrfObj));
    return RefSrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to refine a curve.						     M
*   If, however, Replace is non zero, KnotsObj REPLACES current vector.      M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:     Curve to refine.                     			     M
*   RReplace:   If TRUE KnotsObj will replace the knot vector of CrvObj.     M
*		Otherwise, the knots in KnotsObj will be added to it.	     M
*   KnotsObj:   A list of knots.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A refined curve, or a curve with a replaced knot     M
*                       vector.                                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   RefineCurveObject                                                        M
*****************************************************************************/
IPObjectStruct *RefineCurveObject(IPObjectStruct *CrvObj,
				  RealType *RReplace,
				  IPObjectStruct *KnotsObj)
{
    int n,
	Replace = REAL_PTR_TO_INT(RReplace);
    char *ErrStr, Line[LINE_LEN];
    CagdRType
	*t = GetKnotVector(KnotsObj, 0, &n, &ErrStr);
    CagdCrvStruct *RefCrv;
    IPObjectStruct *RefCrvObj;

    if (t == NULL) {
	IPFreeObject(CrvObj);
	sprintf(Line, "REFINE: %s, empty object result.\n", ErrStr);
	IritPrsrFatalError(Line);
	return NULL;
    }
    RefCrv = CagdCrvRefineAtParams(CrvObj -> U.Crvs, Replace, t, n);
    IritFree((VoidPtr) t);
    if (RefCrv == NULL)
	return NULL;

    RefCrvObj = GenCRVObject(RefCrv),
    AttrSetObjectColor(RefCrvObj, AttrGetObjectColor(CrvObj));
    return RefCrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to evaluate surface in specified parameter values.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:    Surface to evaluate at (u, v).                                M
*   u, v:      Parameter values to evaluate at.                              M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A control point of the same type SrfObj has.          M
*                                                                            *
* KEYWORDS:                                                                  M
*   EvalSurfaceObject                                                        M
*****************************************************************************/
IPObjectStruct *EvalSurfaceObject(IPObjectStruct *SrfObj,
				  RealType *u,
				  RealType *v)
{
    CagdRType
	*Pt = CagdSrfEval(SrfObj -> U.Srfs, *u, *v);
    IPObjectStruct
	*CtlPtObj = GenCTLPTObject(SrfObj -> U.Srfs -> PType, Pt, NULL);

    return CtlPtObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to evaluate curve in specified parameter value.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:    Surface to evaluate at t.                                     M
*   t:         Parameter value to evaluate at.                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A control point of the same type CrvObj has.          M
*                                                                            *
* KEYWORDS:                                                                  M
*   EvalPCurveObject                                                         M
*****************************************************************************/
IPObjectStruct *EvalCurveObject(IPObjectStruct *CrvObj, RealType *t)
{
    CagdRType
	*Pt = CagdCrvEval(CrvObj -> U.Crvs, *t);
    IPObjectStruct
	*CtlPtObj = GenCTLPTObject(CrvObj -> U.Crvs -> PType, Pt, NULL);

    return CtlPtObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to compute the derivative surface in Dir of SrfObj.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:    Surface to differentiate.                                     M
*   Dir:       Direction of differentiation. Either U or V.                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A differentiated surface.                            M
*                                                                            *
* KEYWORDS:                                                                  M
*   DeriveSurfaceObject                                                      M
*****************************************************************************/
IPObjectStruct *DeriveSurfaceObject(IPObjectStruct *SrfObj, RealType *Dir)
{
    CagdSrfStruct
	*DerivSrf = CagdSrfDerive(SrfObj -> U.Srfs, REAL_PTR_TO_INT(Dir));
    IPObjectStruct
	*DerivSrfObj = GenSRFObject(DerivSrf);

    return DerivSrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to compute the derivative curve of CrvObj.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:    Curve to differentiate.                                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    A differentiated curve.                             M
*                                                                            *
* KEYWORDS:                                                                  M
*   DeriveCurveObject                                                        M
*****************************************************************************/
IPObjectStruct *DeriveCurveObject(IPObjectStruct *CrvObj)
{
    CagdCrvStruct
	*DerivCrv = CagdCrvDerive(CrvObj -> U.Crvs);
    IPObjectStruct
	*DerivCrvObj = GenCRVObject(DerivCrv);

    return DerivCrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to compute the integral curve of CrvObj.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:    Curve to integrate.                                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Integrated curve.                                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   IntegrateCurveObject                                                     M
*****************************************************************************/
IPObjectStruct *IntegrateCurveObject(IPObjectStruct *CrvObj)
{
    CagdCrvStruct
	*DerivCrv = CagdCrvIntegrate(CrvObj -> U.Crvs);
    IPObjectStruct
	*DerivCrvObj = GenCRVObject(DerivCrv);

    return DerivCrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to compute the normal surface of SrfObj.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:    Surface to compute a normal vector field for.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A vector field surface representing SrfObj's normal. M
*                                                                            *
* KEYWORDS:                                                                  M
*   SurfaceNormalObject                                                      M
*****************************************************************************/
IPObjectStruct *SurfaceNormalObject(IPObjectStruct *SrfObj)
{
    CagdSrfStruct
	*NormalSrf = SymbSrfNormalSrf(SrfObj -> U.Srfs);
    IPObjectStruct
	*NormalSrfObj = GenSRFObject(NormalSrf);

    return NormalSrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to evaluate surface's normal in specified parameter values.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:   Surface to evaluate its normal at (u, v).                      M
*   u, v:     Parameters at which to evaluate SrfObj's normal.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A vector object representing the normal of SrfObj     M
*                      at (u, v).                                            M
*                                                                            *
* KEYWORDS:                                                                  M
*   NormalSurfaceObject                                                      M
*****************************************************************************/
IPObjectStruct *NormalSurfaceObject(IPObjectStruct *SrfObj,
				    RealType *u,
				    RealType *v)
{
    int i;
    RealType V[3];
    CagdVecStruct
	*Vec = CagdSrfNormal(SrfObj -> U.Srfs, *u, *v);
    IPObjectStruct *NormalObj;

    for (i = 0; i < 3; i++)
	V[i] = Vec -> Vec[i];

    NormalObj = GenVECObject(&V[0], &V[1], &V[2]);

    return NormalObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to evaluate surface's tangent in specified parameter value and dir.M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:   Surface to evaluate its tangent at (u, v).                     M
*   RDir:     Direction of tangent, Either U or V.                           M
*   u, v:     Parameters at which to evaluate SrfObj's tangent.              M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A vector object representing the tangent of SrfObj   M
*                       at (u, v) in direction RDir.                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   TangentSurfaceObject                                                     M
*****************************************************************************/
IPObjectStruct *TangentSurfaceObject(IPObjectStruct *SrfObj,
				     RealType *RDir,
				     RealType *u,
				     RealType *v)
{
    int i,
	Dir = REAL_PTR_TO_INT(RDir);
    RealType V[3];
    CagdVecStruct
	*Vec = CagdSrfTangent(SrfObj -> U.Srfs, *u, *v, Dir);
    IPObjectStruct *TangentObj;

    for (i = 0; i < 3; i++)
	V[i] = Vec -> Vec[i];

    TangentObj = GenVECObject(&V[0], &V[1], &V[2]);

    return TangentObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to evaluate curve's tangent in specified parameter value.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:    Curve to evaluate its tangent at t.                           M
*   t:         Parameter to evaluate CrvObj at.                              M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A vector object representing the tangent of CrvObj.  M
*                                                                            *
* KEYWORDS:                                                                  M
*   TangentCurveObject                                                       M
*                                                                            M
*****************************************************************************/
IPObjectStruct *TangentCurveObject(IPObjectStruct *CrvObj, RealType *t)
{
    int i;
    RealType V[3];
    CagdVecStruct
	*Vec = CagdCrvTangent(CrvObj -> U.Crvs, *t);
    IPObjectStruct *TangentObj;

    for (i = 0; i < 3; i++)
	V[i] = Vec -> Vec[i];

    TangentObj = GenVECObject(&V[0], &V[1], &V[2]);

    return TangentObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to extract an isoparametric curve out of a surface.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:     Surface to extract an isoparametric curve from.              M
*   RDir:       Direction of extraction. Either U or V.                      M
*   ParamVal:   Parameter value of isoparametric curve.                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A curve object which is an isoparametric curve of     M
*                      SrfObj.                                               M
*                                                                            *
* KEYWORDS:                                                                  M
*   CurveFromSurface                                                         M
*****************************************************************************/
IPObjectStruct *CurveFromSurface(IPObjectStruct *SrfObj,
				 RealType *RDir,
				 RealType *ParamVal)
{
    int Dir = REAL_PTR_TO_INT(RDir);
    CagdCrvStruct
	*Crv = CagdCrvFromSrf(SrfObj -> U.Srfs, *ParamVal, Dir);
    IPObjectStruct *CrvObj;

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to extract an isoparametric curve out of a surface mesh.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:     Surface to extract an isoparametric curve from itsmesh.      M
*   RDir:       Direction of extraction. Either U or V.                      M
*   RIndex:     Index into SrfObj's mesh.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A curve object which is not necessarily in SrfObj.    M
*                                                                            *
* KEYWORDS:                                                                  M
*   CurveFromSrfMesh                                                         M
*****************************************************************************/
IPObjectStruct *CurveFromSrfMesh(IPObjectStruct *SrfObj,
				 RealType *RDir,
				 RealType *RIndex)
{
    int Dir = REAL_PTR_TO_INT(RDir),
	Index = REAL_PTR_TO_INT(RIndex);
    CagdCrvStruct
	*Crv = CagdCrvFromMesh(SrfObj -> U.Srfs, Index, Dir);
    IPObjectStruct *CrvObj;

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}
