/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
*   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 "extra_fn.h"
#include "allocate.h"
#include "attribut.h"
#include "objects.h"
#include "primitiv.h"
#include "ip_cnvrt.h"
#include "user_lib.h"
#include "freeform.h"

static IPObjectStruct *GetControlTVMesh(IPObjectStruct *LstLstObjList,
					int UOrder,
					int VOrder,
					int WOrder,
					TrivGeomType GType,
					char **ErrStr);
static IPObjectStruct *KnotVectorToListObj(CagdRType *KV, int KVLen);

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Construct a trimmed surface, given the parametric surface and its	     M
* trimming curves.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:         To construct the trimmed surface with.                   M
*   TrimmedCrvsObj: To trim srf with. No real validity testing is performed. M
*		    Can be NULL Create a structure of trimmed surface,       M
*		    untrimmed.						     M
*   RHasTopLvlTrim: FALSE if needs to add a top level rectangular boundary.  M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A trimmed surface object.                            M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenTrimmedSurface                                                        M
*****************************************************************************/
IPObjectStruct *GenTrimmedSurface(IPObjectStruct *SrfObj,
				  IPObjectStruct *TrimmedCrvsObj,
				  RealType *RHasTopLvlTrim)
{
    CagdCrvStruct
	*Crvs = NULL;
    CagdBType
	HasTopLvlTrim = REAL_PTR_TO_INT(RHasTopLvlTrim);

    if (IP_IS_CRV_OBJ(TrimmedCrvsObj)) {
	Crvs = CagdCrvCopyList(TrimmedCrvsObj -> U.Crvs);
    }
    else if (IP_IS_OLST_OBJ(TrimmedCrvsObj)) {
	int NumCrvs = 0;
	IPObjectStruct *CrvObj;

	while ((CrvObj = ListObjectGet(TrimmedCrvsObj, NumCrvs++)) != NULL) {
	    CagdCrvStruct *Crv;

	    if (!IP_IS_CRV_OBJ(CrvObj)) {
		IritNonFatalError("Non curve object as a trimmed curve");
		return NULL;
	    }

	    Crv = CagdCrvCopy(CrvObj -> U.Crvs);
	    LIST_PUSH(Crv, Crvs);
	}
    }

    return GenTRIMSRFObject(TrimSrfNew2(CagdSrfCopy(SrfObj -> U.Srfs),
					Crvs, HasTopLvlTrim));
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Extracts the parametric surface of a trimmed surface.                    M
*                                                                            *
* PARAMETERS:                                                                M
*   TrimmedSrfObj:   To extract parametric surface from.                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Extracted parametric surface.                       M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetSrfFromTrimmedSrf                                                     M
*****************************************************************************/
IPObjectStruct *GetSrfFromTrimmedSrf(IPObjectStruct *TrimmedSrfObj)
{
    return GenSRFObject(CagdSrfCopy(TrimmedSrfObj -> U.TrimSrfs -> Srf));
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Extracts trimming curves of trimming surface, either in parametric or    M
* Euclidean space.                                                           M
*                                                                            *
* PARAMETERS:                                                                M
*   TrimmedSrfObj:   To extract trimming curves from.                        M
*   RParamSpace:     TRUE for parametric space, FALSE for Euclidean space.   M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Extracted trimming curves.                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetTrimCrvsFromTrimmedSrf                                                M
*****************************************************************************/
IPObjectStruct *GetTrimCrvsFromTrimmedSrf(IPObjectStruct *TrimmedSrfObj,
					  RealType *RParamSpace)
{
    int Count = 0,
	ParamSpace = REAL_PTR_TO_INT(RParamSpace);
    CagdCrvStruct
	*Crvs = TrimGetTrimmingCurves(TrimmedSrfObj -> U.TrimSrfs,
				      ParamSpace, TRUE);
    IPObjectStruct
	*ListObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    while (Crvs) {
	CagdCrvStruct
	    *Crv = Crvs;

	Crvs = Crvs -> Pnext;
	Crv -> Pnext = NULL;
	ListObjectInsert(ListObj, Count++, GenCRVObject(Crv));
    }

    ListObjectInsert(ListObj, Count, NULL);
    return ListObj;
}

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

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

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

	    NumVertices = -1;
	    while ((PtObj = ListObjectGet(ObjList, ++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(ObjList, PtType)) ==
								CAGD_PT_NONE) {
		*ErrStr = "";
		return NULL;
	    }

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

	if (NumListLists++ == 0)
	    NumListsFirst = NumLists;
	else if (NumListsFirst != NumLists) {
	    *ErrStr = "Different size of list lists";
	    return NULL;
	}
    }

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

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

    TrivarObj = GenTRIVARObject(NULL);
    switch (GType) {
	case TRIV_TVBEZIER_TYPE:
	    TrivarObj -> U.Trivars = TrivBzrTVNew(NumVertices, NumLists,
						  NumListLists, PtType);
	    break;
	case TRIV_TVBSPLINE_TYPE:
	    TrivarObj -> U.Trivars = TrivBspTVNew(NumVertices, NumLists,
						  NumListLists, UOrder,
						  VOrder, WOrder, PtType);
	    break;
	default:
	    break;
    }
    AttrSetObjectColor(TrivarObj, GlblPrimColor);  /* Set its default color. */
    PtSize = CAGD_IS_RATIONAL_PT(PtType) + CAGD_NUM_OF_PT_COORD(PtType);

    NumPlane = NumVertices * NumLists;
    for (r = TrivarObj -> U.Trivars -> Points, i = 0; i < NumListLists; i++) {
	LstObjList = ListObjectGet(LstLstObjList, i);

        for (j = 0; j < NumLists; j++) {
	    IPObjectStruct
		*ObjList = ListObjectGet(LstObjList, j);

	    for (k = 0; k < NumVertices; k++) {
		IPObjectStruct
		    *VObj = ListObjectGet(ObjList, k);

		v = VObj -> U.CtlPt.Coords;

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

    return TrivarObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to create a Bezier trivar geometric object defined by a list of  M
* lists of lists of control points.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   LstLstObjList: A list object of lists of lists of control points.        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A Bezier trivar object if successful, NULL otherwise.  M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBezierTrivarObject                                                    M
*****************************************************************************/
IPObjectStruct *GenBezierTrivarObject(IPObjectStruct *LstLstObjList)
{
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*TrivObj = GetControlTVMesh(LstLstObjList, -1, -1, -1,
				    TRIV_TVBEZIER_TYPE, &ErrStr);

    if (TrivObj == NULL) {
	sprintf(Line, "TBEZIER: %s, empty object result.\n", ErrStr);
	IritNonFatalError(Line);
    }

    return TrivObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to create a Bspline trivar geometric object defined by a list    M
* of lists of lists of control points.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   RUOrder:     U order of trivar.                                          M
*   RVOrder:     V order of trivar.                                          M
*   RWOrder:     W order of trivar.                                          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 trivar object if successful, NULL otherwise. M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBsplineTrivarObject                                                   M
*****************************************************************************/
IPObjectStruct *GenBsplineTrivarObject(RealType *RUOrder,
				       RealType *RVOrder,
				       RealType *RWOrder,
				       IPObjectStruct *LstLstObjList,
				       IPObjectStruct *KntObjList)
{
    int Len1, Len2, Len3,
	UOrder = REAL_PTR_TO_INT(RUOrder),
	VOrder = REAL_PTR_TO_INT(RVOrder),
	WOrder = REAL_PTR_TO_INT(RWOrder);
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*TrivObj = GetControlTVMesh(LstLstObjList, UOrder, VOrder, WOrder,
				    TRIV_TVBSPLINE_TYPE, &ErrStr);

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

    if (!IP_IS_OLST_OBJ(KntObjList) || ListObjectLength(KntObjList) != 3) {
	IPFreeObject(TrivObj);
	IritNonFatalError("TBSPLINE: Exactly three knot vectors expected");
	return NULL;
    }

    if (TrivObj -> U.Trivars -> ULength < TrivObj -> U.Trivars -> UOrder ||
	TrivObj -> U.Trivars -> VLength < TrivObj -> U.Trivars -> VOrder ||
	TrivObj -> U.Trivars -> WLength < TrivObj -> U.Trivars -> WOrder) {
	IPFreeObject(TrivObj);
	IritNonFatalError("TBSPLINE: Trivar mesh length smaller than order.");
	return NULL;
    }

    IritFree((VoidPtr) TrivObj -> U.Trivars -> UKnotVector);
    TrivObj -> U.Trivars -> UKnotVector = NULL;
    IritFree((VoidPtr) TrivObj -> U.Trivars -> VKnotVector);
    TrivObj -> U.Trivars -> VKnotVector = NULL;
    IritFree((VoidPtr) TrivObj -> U.Trivars -> WKnotVector);
    TrivObj -> U.Trivars -> WKnotVector = NULL;
    Len1 = TrivObj -> U.Trivars -> ULength;
    Len2 = TrivObj -> U.Trivars -> VLength;
    Len3 = TrivObj -> U.Trivars -> WLength;
    if ((TrivObj -> U.Trivars -> UKnotVector =
	 GetKnotVector(ListObjectGet(KntObjList, 0), UOrder,
		       &Len1, &ErrStr)) == NULL ||
	(TrivObj -> U.Trivars -> VKnotVector =
	 GetKnotVector(ListObjectGet(KntObjList, 1), VOrder,
		       &Len2, &ErrStr)) == NULL ||
	(TrivObj -> U.Trivars -> WKnotVector =
	 GetKnotVector(ListObjectGet(KntObjList, 2), WOrder,
		       &Len3, &ErrStr)) == NULL) {
	sprintf(Line, "TBSPLINE: Knot vectors, %s, empty object result.\n",
		ErrStr);
	IPFreeObject(TrivObj);
	IritNonFatalError(Line);
	return NULL;
    }

    if (Len1 != TrivObj -> U.Trivars -> ULength + UOrder) {
	if (Len1 == TrivObj -> U.Trivars -> ULength + UOrder + UOrder - 1)
	    TrivObj -> U.Trivars -> UPeriodic = TRUE;
	else {
	    IPFreeObject(TrivObj);
	    IritNonFatalError("Wrong knot vector length");
	    return NULL;
	}
    }
    if (Len2 != TrivObj -> U.Trivars -> VLength + VOrder) {
	if (Len2 == TrivObj -> U.Trivars -> VLength + VOrder + VOrder - 1)
	    TrivObj -> U.Trivars -> VPeriodic = TRUE;
	else {
	    IPFreeObject(TrivObj);
	    IritNonFatalError("Wrong knot vector length");
	    return NULL;
	}
    }
    if (Len3 != TrivObj -> U.Trivars -> WLength + WOrder) {
	if (Len3 == TrivObj -> U.Trivars -> WLength + WOrder + WOrder - 1)
	    TrivObj -> U.Trivars -> WPeriodic = TRUE;
	else {
	    IPFreeObject(TrivObj);
	    IritNonFatalError("Wrong knot vector length");
	    return NULL;
	}
    }

    return TrivObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Evaluate a trivariate function, at the prescribed parametric location.   M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:    Trivariate to evaluate.                                        M
*   u, v, w:  Parametric location to evaluate at.                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A control point of the same type TV has.              M
*                                                                            *
* KEYWORDS:                                                                  M
*   EvalTrivarObject                                                         M
*****************************************************************************/
IPObjectStruct *EvalTrivarObject(IPObjectStruct *TVObj,
				 RealType *u,
				 RealType *v,
				 RealType *w)
{
    CagdRType
	*Pt = TrivTVEval(TVObj -> U.Trivars, *u, *v, *w);
    IPObjectStruct
	*CtlPtObj = GenCTLPTObject(TVObj -> U.Trivars -> PType, Pt, NULL);

    return CtlPtObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Extracts an isoparametric surface out of the trivariate.                 M
*                                                                            M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:     Trivariate to extract surface from.                           M
*   RDir:      Direction of extarction. One of U, V, W.                      M
*   ParamVal:  Parameter value of trivariate.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   An isoparametric surface.                            M
*                                                                            *
* KEYWORDS:                                                                  M
*   SurfaceFromTrivar                                                        M
*****************************************************************************/
IPObjectStruct *SurfaceFromTrivar(IPObjectStruct *TVObj,
				  RealType *RDir,
				  RealType *ParamVal)
{
    TrivTVDirType
	Dir = (TrivTVDirType) REAL_PTR_TO_INT(RDir);
    CagdSrfStruct
	*Srf = TrivSrfFromTV(TVObj -> U.Trivars, *ParamVal, Dir);

    if (Srf == NULL)
	return NULL;

    return GenSRFObject(Srf);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to subdivide a trivariate function into two in specified direction M
* (1 or 2) and specified parameter value.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:        Trivariate to subdivide.                                   M
*   RDir:         Direction of subdivision. Either U, V, or W.               M
*   ParamVal:     Parameter value at which subdivision should occur.         M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object of two trivar objects, result of the    M
*		       subdivision.					     M
*                                                                            *
* KEYWORDS:                                                                  M
*   DivideTrivarObject                                                       M
*****************************************************************************/
IPObjectStruct *DivideTrivarObject(IPObjectStruct *TVObj,
				    RealType *RDir,
				    RealType *ParamVal)
{
    TrivTVDirType
	Dir = (TrivTVDirType) REAL_PTR_TO_INT(RDir);
    TrivTVStruct
	*TV = TrivTVSubdivAtParam(TVObj -> U.Trivars, *ParamVal, Dir);
    IPObjectStruct *TV1, *TV2, *TVList;

    if (TV == NULL)
	return NULL;

    TV1 = GenTRIVARObject(TV),
    AttrSetObjectColor(TV1, AttrGetObjectColor(TVObj));
    TV2 = GenTRIVARObject(TV -> Pnext),
    AttrSetObjectColor(TV2, AttrGetObjectColor(TVObj));
    TV -> Pnext = NULL;

    TVList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
    ListObjectInsert(TVList, 0, TV1);
    ListObjectInsert(TVList, 1, TV2);
    ListObjectInsert(TVList, 2, NULL);

    return TVList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to extract a region of a trivariate in specified direction (1 or   M
* 2) and specified parameter values.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:       Trivariate 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 TVObj,                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   RegionFromTrivarObject                                                   M
*****************************************************************************/
IPObjectStruct *RegionFromTrivarObject(IPObjectStruct *TVObj, 
					RealType *RDir,
					RealType *ParamVal1,
					RealType *ParamVal2)
{
    TrivTVDirType
	Dir = (TrivTVDirType) REAL_PTR_TO_INT(RDir);
    TrivTVStruct
	*TV = TrivTVRegionFromTV(TVObj -> U.Trivars,
				 *ParamVal1, *ParamVal2, Dir);

    if (TV == NULL)
	return NULL;

    TVObj = GenTRIVARObject(TV);

    return TVObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to refine a trivariate function in specified direction (1 or 2)    M
* and knot vector.							     M
*   If, however, Replace is non zero, KnotsObj REPLACES current vector.      M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:      Trivariate 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 TVObj. M
*		Otherwise, the knots in KnotsObj will be added to it.	     M
*   KnotsObj:   A list of knots.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A refined trivar, or a trivar with a replaced knot   M
*                       vector.                                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   RefineTrivarObject                                                       M
*****************************************************************************/
IPObjectStruct *RefineTrivarObject(IPObjectStruct *TVObj,
				    RealType *RDir,
				    RealType *RReplace,
				    IPObjectStruct *KnotsObj)
{
    int n,
	Replace = REAL_PTR_TO_INT(RReplace);
    TrivTVDirType
	Dir = (TrivTVDirType) REAL_PTR_TO_INT(RDir);
    char *ErrStr, Line[LINE_LEN];
    CagdRType
	*t = GetKnotVector(KnotsObj, 0, &n, &ErrStr);
    TrivTVStruct *RefTV;
    IPObjectStruct *RefTVObj;

    if (t == NULL) {
	IPFreeObject(TVObj);
	sprintf(Line, "REFINE: %s, empty object result.\n", ErrStr);
	IritNonFatalError(Line);
	return NULL;
    }
    RefTV = TrivTVRefineAtParams(TVObj -> U.Trivars, Dir, Replace, t, n);
    IritFree((VoidPtr) t);
    if (RefTV == NULL)
	return NULL;

    RefTVObj = GenTRIVARObject(RefTV),
    AttrSetObjectColor(RefTVObj, AttrGetObjectColor(TVObj));
    return RefTVObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to differentiate a trivariate function in Dir of SrfObj.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:     Trivar to differentiate.                                      M
*   Dir:       Direction of differentiation. Either U or V or W.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A differentiated trivar.                             M
*                                                                            *
* KEYWORDS:                                                                  M
*   DeriveTrivarObject                                                       M
*****************************************************************************/
IPObjectStruct *DeriveTrivarObject(IPObjectStruct *TVObj, RealType *Dir)
{
    TrivTVStruct
	*DerivTV = TrivTVDerive(TVObj -> U.Trivars,
				(TrivTVDirType) REAL_PTR_TO_INT(Dir));
    IPObjectStruct
	*DerivTVObj = GenTRIVARObject(DerivTV);

    return DerivTVObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Interpolates a three dimensional grid.                                   M
*                                                                            *
* PARAMETERS:                                                                M
*   TVObj:     Trivariate to interpolate its control points.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Interpolating trivar, with same order as original.   M
*                                                                            *
* KEYWORDS:                                                                  M
*   InterpolateTrivar                                                        M
*****************************************************************************/
IPObjectStruct *InterpolateTrivar(IPObjectStruct *TVObj)
{
    TrivTVStruct
	*NewTV = TrivInterpTrivar(TVObj -> U.Trivars);

    if (NewTV == NULL)
	return NULL;

    return GenTRIVARObject(NewTV);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes moments of freeform curves.                                     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:    Curve to compute moment for.                                  M
*   RMoment:   Order of moment.                                              M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   An E3 vector representing the approximated moment.   M
*                                                                            *
* KEYWORDS:                                                                  M
*   ComputeCrvMoments, moments                                               M
*****************************************************************************/
IPObjectStruct *ComputeCrvMoments(IPObjectStruct *CrvObj, RealType *RMoment)
{
    CagdPType Pt;
    CagdVType Vec;
    int Moment = REAL_PTR_TO_INT(RMoment);

    CagdCrvFirstMoments(CrvObj -> U.Crvs, 100, Pt, Vec);

    switch (Moment) {
	case 0:
	    return GenPTObject(&Pt[0], &Pt[1], &Pt[2]);
	case 1:
	    return GenVECObject(&Vec[0], &Vec[1], &Vec[2]);
	default:
	    IritNonFatalError("Moment: Only moments of order zero or one");
	    return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Construct a trivariate out of the provided surface list.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvList:     A list of surfaces to approximate a trivariate through.     M
*   OtherOrder:  Other, third, order of trivariate.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A trivariate approximately traversing through the    M
*			given surfaces.                                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenTVFromSrfsObject                                                      M
*****************************************************************************/
IPObjectStruct *GenTVFromSrfsObject(IPObjectStruct *SrfList,
				    RealType *OtherOrder)
{
    int i,
	NumSrfs = 0;
    IPObjectStruct *TVObj, *SrfObj;
    TrivTVStruct *TV;
    CagdSrfStruct *Srf,
	*Srfs = NULL;


    if (!IP_IS_OLST_OBJ(SrfList))
	IritFatalError("TFROMSRF: Not object list object!");

    while ((SrfObj = ListObjectGet(SrfList, NumSrfs)) != NULL) {
	if (!IP_IS_SRF_OBJ(SrfObj)) {
	    IritNonFatalError("TFROMSRF: List contains non surface object(s).");
	    return NULL;
	}
	if (SrfObj -> U.Srfs -> Pnext != NULL) {
	    IritNonFatalError("TFROMSRF: nested surface lists are disallowed.");
	    return NULL;
	}
	NumSrfs++;
    }

    /* Chain all surfaces into a single list and invoke the TV constructor: */
    for (i = 0; i < NumSrfs; i++) {
	SrfObj = ListObjectGet(SrfList, i);
	Srf = CagdSrfCopy(SrfObj -> U.Srfs);
	LIST_PUSH(Srf, Srfs);
    }

    Srfs = CagdListReverse(Srfs);
    TV = TrivTVFromSrfs(Srfs, REAL_PTR_TO_INT(OtherOrder));
    CagdSrfFreeList(Srf);

    if (TV == NULL)
	return NULL;

    TVObj = GenTRIVARObject(TV);

    return TVObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Creates a cubic Hermite curve or surface out of the given two positional M
* and two directional constraints. The constraints can be either	     M
* points/vectors where a cubic Bezier curve is constructed or four curves    M
* where a surface is constructed.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   Pos1:    Starting position.                                              M
*   Pos2:    End position.                                                   M
*   Dir1:    Tangent at starting position.                                   M
*   Dir2:    Tangent at end position.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A curve or surface satisfying these four constraints. M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenHermiteObject, Hermite                                                M
*****************************************************************************/
IPObjectStruct *GenHermiteObject(IPObjectStruct *Pos1,
				 IPObjectStruct *Pos2,
				 IPObjectStruct *Dir1,
				 IPObjectStruct *Dir2)
{
    IPObjectStruct *PObj;

    if (IP_IS_CRV_OBJ(Pos1) &&
	IP_IS_CRV_OBJ(Pos2) &&
	IP_IS_CRV_OBJ(Dir1) &&
	IP_IS_CRV_OBJ(Dir2)) {
	CagdSrfStruct
	    *Srf = CagdCubicHermiteSrf(Pos1 -> U.Crvs, Pos2 -> U.Crvs,
				       Dir1 -> U.Crvs, Dir2 -> U.Crvs);

	PObj = GenSRFObject(Srf);
    }
    else if ((IP_IS_POINT_OBJ(Pos1) || IP_IS_VEC_OBJ(Pos1)) &&
	     (IP_IS_POINT_OBJ(Pos2) || IP_IS_VEC_OBJ(Pos2)) &&
	     (IP_IS_POINT_OBJ(Dir1) || IP_IS_VEC_OBJ(Dir1)) &&
	     (IP_IS_POINT_OBJ(Dir2) || IP_IS_VEC_OBJ(Dir2))) {
	CagdCrvStruct
	    *Crv = CagdCubicHermiteCrv(Pos1 -> U.Pt, Pos2 -> U.Pt,
				       Dir1 -> U.Vec, Dir2 -> U.Vec);

	PObj = GenCRVObject(Crv);
    }
    else {
	IritFatalError("HERMITE: Invalid parameters!");
	PObj = NULL;
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Reparametrize the second curve into the "best" match to the first curve. M
*                                                                            *
* PARAMETERS:                                                                M
*   PCrv1, PCrv2:  The two curve to establish matching between.              M
*   RReduce:       Accuracy of matching, the larger the better.              M
*   RSampleSet:    Number of samples of tangents.			     M
*   RReparamOrder: Order of reparametrization curve.			     M
*   RRotate:       If one would like to apply rotation.			     M
*   RNorm:         1 for ruled norm, 2 for morph norm.			     M
*                                                                            *
* RETURN VALUE:                                                              M
*   Reparametrized crv2 to match crv1.                                       M
*                                                                            *
* KEYWORDS:                                                                  M
*   MatchTwoCurves                                                           M
*****************************************************************************/
IPObjectStruct *MatchTwoCurves(IPObjectStruct *PCrv1,
			       IPObjectStruct *PCrv2,
			       RealType *RReduce,
			       RealType *RSampleSet,
			       RealType *RReparamOrder,
			       RealType *RRotate,
			       RealType *RNorm)
{
    int AllowNegativeNorm,
	Reduce = REAL_PTR_TO_INT(RReduce),
	SampleSet = REAL_PTR_TO_INT(RSampleSet),
	Rotate = REAL_PTR_TO_INT(RRotate),
	ReparamOrder = REAL_PTR_TO_INT(RReparamOrder),
	Norm = REAL_PTR_TO_INT(RNorm);
    CagdMatchNormFuncType
        MatchNorm = NULL;
    CagdCrvStruct *Crv;

    switch (ABS(Norm)) {
	case 1:
	    MatchNorm = CagdMatchRuledNorm;
	    break;
	case 2:
	    MatchNorm = CagdMatchMorphNorm;
	    break;
	case 4:
	    MatchNorm = CagdMatchBisectorNorm;
	    break;
	case 3:
	default:
	    MatchNorm = CagdMatchDistNorm;
	    break;
    }
    AllowNegativeNorm = Norm < 0;

    Crv = CagdMatchingTwoCurves(PCrv1 -> U.Crvs, PCrv2 -> U.Crvs,
				Reduce, SampleSet, ReparamOrder,
				Rotate, AllowNegativeNorm, MatchNorm);

    if (Crv == NULL) {
	WndwInputWindowPutStr("FFMATCH: failed to match the two curves.");
	return NULL;
    }

    return GenCRVObject(Crv);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Contours a freeform surface according to prescribed Plane.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PSrfObj:   Freefrom object to contour. Object is approximated using      M
*	       global resolution level, before contoured.		     M
*   Plane:     To contour PSrfObj with.					     M
*   PSrfEvalObj: If non NULL and PSrfObj is a scalar surface, the resulting  M
*	       contoured data is mapped onto this surface.		     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A polyline approximation of the contoured data.       M
*                                                                            *
* KEYWORDS:                                                                  M
*   ContourFreeform                                                          M
*****************************************************************************/
IPObjectStruct *ContourFreeform(IPObjectStruct *PSrfObj,
				PlaneType Plane,
				IPObjectStruct *PSrfEvalObj)
{
    int Resolution = GetResolution(FALSE);
    RealType
        RelResolution = AttrGetObjectRealAttrib(PSrfObj, "resolution");
    IPPolygonStruct *Cntrs, *Cntr;
    IPVertexStruct *V;
    IPObjectStruct *CntrObj;

    if (Resolution < MIN_FREE_FORM_RES)
        Resolution = MIN_FREE_FORM_RES;
    if (RelResolution < IP_ATTR_BAD_REAL)
        Resolution = REAL_TO_INT(Resolution * RelResolution);

    if (!IP_IS_SRF_OBJ(PSrfObj))
        return NULL;

    Cntrs = UserCntrSrfWithPlane(PSrfObj -> U.Srfs, Plane, Resolution);

    if (Cntrs == NULL)
	return NULL;

    if (CAGD_NUM_OF_PT_COORD(PSrfObj -> U.Srfs -> PType) == 1) {
	if (PSrfEvalObj != NULL) {
	    CagdSrfStruct
	        *Srf = PSrfEvalObj -> U.Srfs;

            for (Cntr = Cntrs; Cntr != NULL; Cntr = Cntr -> Pnext) {
                for (V = Cntr -> PVertex; V != NULL; V = V -> Pnext) {
		    CagdRType
		        *P = CagdSrfEval(Srf, V -> Coord[1], V -> Coord[2]);

		    CagdCoerceToE3(V -> Coord, &P, -1, Srf -> PType);
		}
	    }
	}
	else {
	    for (Cntr = Cntrs; Cntr != NULL; Cntr = Cntr -> Pnext) {
		for (V = Cntr -> PVertex; V != NULL; V = V -> Pnext) {
		    V -> Coord[0] = V -> Coord[1];
		    V -> Coord[1] = V -> Coord[2];
		    V -> Coord[2] = 0.0;
		}
	    }
        }
    }

    CntrObj = GenPOLYObject(Cntrs);
    IP_SET_POLYLINE_OBJ(CntrObj);
    return CntrObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes the intersection point of a ray and a freeform surface.         M
*   The surface is approximated by polygons that are then tested for the     M
* intersection.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:        Surface to shoot a ray against.                              M
*   RayOrigin:  Origin position of ray.                                      M
*   RayDir:     Direction of ray.	                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A point holding the UV values (in XY) of the first    M
*		ray surface intersection if was one, NULL otherwise.	     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SurfaceRayIntersect                                                      M
*****************************************************************************/
IPObjectStruct *SurfaceRayIntersect(IPObjectStruct *PSrfObj,
				    PointType RayOrigin,
				    VectorType RayDir)
{
    int Resolution = GetResolution(FALSE);
    CagdUVType UV;
    RealType
        RelResolution = AttrGetObjectRealAttrib(PSrfObj, "resolution");
    VoidPtr Handle;
    IPObjectStruct *PObj;

    if (Resolution < MIN_FREE_FORM_RES)
        Resolution = MIN_FREE_FORM_RES;
    if (RelResolution < IP_ATTR_BAD_REAL)
        Resolution = REAL_TO_INT(Resolution * RelResolution);

    Handle = IntrSrfRayPreprocessSrf(PSrfObj -> U.Srfs, Resolution);

    if (IntrSrfRayTestRay(Handle, RayOrigin, RayDir, UV)) {
	static RealType
	    R = 0.0;

	PObj = GenPTObject(&UV[0], &UV[1], &R);
    }
    else {
	static PointType
	    Pt = { 0.0, 0.0, 1.0 };

	PObj = GenPTObject(&Pt[0], &Pt[1], &Pt[2]);
    }

    IntrSrfRayFreePreprocess(Handle);

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Splits a given freeform into its scalar componenets.  Returns a list of  M
* all the components as E1 freeforms.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForm:     To split into its individial coefficients.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list of coefficients as freeforms.		     M
*                                                                            *
* SEE ALSO:                                                                  M
*   FreeFormMergeScalar                                                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormSplitScalar                                                      M
*****************************************************************************/
IPObjectStruct *FreeFormSplitScalar(IPObjectStruct *FreeForm)
{
    IPObjectStruct
	*RetVal = NULL;
    int i = 0;

    if (IP_IS_CRV_OBJ(FreeForm)) {
	CagdCrvStruct *CrvW, *CrvX, *CrvY, *CrvZ;

	SymbCrvSplitScalar(FreeForm -> U.Crvs, &CrvW, &CrvX, &CrvY, &CrvZ);
	RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
	if (CrvW)
	    ListObjectInsert(RetVal, i++, GenCRVObject(CrvW));
	if (CrvX)
	    ListObjectInsert(RetVal, i++, GenCRVObject(CrvX));
	if (CrvY)
	    ListObjectInsert(RetVal, i++, GenCRVObject(CrvY));
	if (CrvZ)
	    ListObjectInsert(RetVal, i++, GenCRVObject(CrvZ));
	ListObjectInsert(RetVal, i++, NULL);
    }
    else if (IP_IS_SRF_OBJ(FreeForm)) {
	CagdSrfStruct *SrfW, *SrfX, *SrfY, *SrfZ;

	SymbSrfSplitScalar(FreeForm -> U.Srfs, &SrfW, &SrfX, &SrfY, &SrfZ);
	RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
	if (SrfW)
	    ListObjectInsert(RetVal, i++, GenSRFObject(SrfW));
	if (SrfX)
	    ListObjectInsert(RetVal, i++, GenSRFObject(SrfX));
	if (SrfY)
	    ListObjectInsert(RetVal, i++, GenSRFObject(SrfY));
	if (SrfZ)
	    ListObjectInsert(RetVal, i++, GenSRFObject(SrfZ));
	ListObjectInsert(RetVal, i++, NULL);
    }
    else
	IritFatalError("FFSplit: Invalid freeform to split!");

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Merges a given list of freeform into a single freeform with point type   M
* PtType. All the components in ScalarFreeFormList must be E1.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   ScalarFreeFormList:   To merge into a single curve of point type PtType. M
*   RPType:		  To merge this list of scalar freeforms into.       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list of coefficients as freeforms.		     M
*                                                                            *
* SEE ALSO:                                                                  M
*   FreeFormSplitScalar                                                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormMergeScalar                                                      M
*****************************************************************************/
IPObjectStruct *FreeFormMergeScalar(IPObjectStruct *ScalarFreeFormList,
				    RealType *RPType)
{
    IPObjStructType
	ObjType;
    IPObjectStruct *PObj,
	*RetVal = NULL;
    int i, j,
	PType = REAL_PTR_TO_INT(RPType);

    ObjType = ListObjectGet(ScalarFreeFormList, 0) -> ObjType;
    if (ObjType != IP_OBJ_CURVE && ObjType != IP_OBJ_SURFACE) {
	IritNonFatalError("FFMerge: Only curves or surfaces!");
	return NULL;
    }

    for (i = 1; (PObj = ListObjectGet(ScalarFreeFormList, i)) != NULL; i++) {
	if (PObj -> ObjType != ObjType) {
	    IritNonFatalError("FFMerge: Different freeforms in list!");
	    return NULL;
	}
    }

    if (CAGD_IS_RATIONAL_PT(PType))
        i--;
    if (i != CAGD_NUM_OF_PT_COORD(PType)) {
	IritNonFatalError("FFMerge: Incorrect number of freeforms in list!");
	return NULL;
    }

    if (ObjType == IP_OBJ_CURVE) {
	CagdCrvStruct *Crv, *Crvs[CAGD_MAX_PT_SIZE];

	i = j = 0;
	if (CAGD_IS_RATIONAL_PT(PType))
	    Crvs[i++] = ListObjectGet(ScalarFreeFormList, j++) -> U.Crvs;
	else
	    Crvs[i++] = NULL;

	while ((PObj = ListObjectGet(ScalarFreeFormList, j++)) != NULL)
	    Crvs[i++] = PObj -> U.Crvs;

	while (i < CAGD_MAX_PT_SIZE)
	    Crvs[i++] = NULL;

	if ((Crv = SymbCrvMergeScalar(Crvs[0], Crvs[1],
				      Crvs[2], Crvs[3])) != NULL)
	    RetVal = GenCRVObject(Crv);
    }
    else if (ObjType == IP_OBJ_SURFACE) {
	CagdSrfStruct *Srf, *Srfs[CAGD_MAX_PT_SIZE];

	i = j = 0;
	if (CAGD_IS_RATIONAL_PT(PType))
	    Srfs[i++] = ListObjectGet(ScalarFreeFormList, j++) -> U.Srfs;
	else
	    Srfs[i++] = NULL;

	while ((PObj = ListObjectGet(ScalarFreeFormList, i++)) != NULL)
	    Srfs[i++] = PObj -> U.Srfs;

	while (i < CAGD_MAX_PT_SIZE)
	    Srfs[i++] = NULL;

	if ((Srf = SymbSrfMergeScalar(Srfs[0], Srfs[1],
				      Srfs[2], Srfs[3])) != NULL)
	    RetVal = GenSRFObject(Srf);
    }
    else
	IritFatalError("FFSplit: Invalid freeform to split!");

    return RetVal;

}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Returns the point type of a given freeform.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForm:   To return its point type.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A numeric object with the point type of Freeform.     M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormPointType                                                        M
*****************************************************************************/
IPObjectStruct *FreeFormPointType(IPObjectStruct *FreeForm)
{
    RealType
        R = 0.0;

    if (IP_IS_CRV_OBJ(FreeForm))
	R = FreeForm -> U.Crvs -> PType;
    else if (IP_IS_SRF_OBJ(FreeForm))
	R = FreeForm -> U.Srfs -> PType;
    else if (IP_IS_TRIVAR_OBJ(FreeForm))
	R = FreeForm -> U.Trivars -> PType;
    else if (IP_IS_TRIMSRF_OBJ(FreeForm))
	R = FreeForm -> U.TrimSrfs -> Srf -> PType;
    else if (IP_IS_TRISRF_OBJ(FreeForm))
	R = FreeForm -> U.TriSrfs -> PType;
    else
	IritFatalError("FFPtType: Invalid freeform to extract point type from!");

    return GenNUMObject(&R);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Returns the order(s) of a given freeform.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForm:   To return its order(s).                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with the order(s) of Freeform.          M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormOrder                                                            M
*****************************************************************************/
IPObjectStruct *FreeFormOrder(IPObjectStruct *FreeForm)
{
    int i = 0;
    IPObjectStruct
	*RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    if (IP_IS_CRV_OBJ(FreeForm))
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Crvs -> Order));
    else if (IP_IS_SRF_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Srfs -> UOrder));
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Srfs -> VOrder));
    }
    else if (IP_IS_TRIVAR_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Trivars -> UOrder));
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Trivars -> VOrder));
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Trivars -> WOrder));
    }
    else if (IP_IS_TRIMSRF_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
		 GenNUMValObject(FreeForm -> U.TrimSrfs -> Srf -> UOrder));
        ListObjectInsert(RetVal, i++,
		 GenNUMValObject(FreeForm -> U.TrimSrfs -> Srf -> VOrder));
    }
    else if (IP_IS_TRISRF_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.TriSrfs -> Order));
    }
    else {
	IritFatalError("FFOrder: Invalid freeform to extract order(s) from!");
	IPFreeObject(RetVal);
	return NULL;
    }

    ListObjectInsert(RetVal, i++, NULL);

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Returns the control polygon/mesh size(s) of a given freeform.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForm:   To return its control polygon/mesh size(s).                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with the control polygon/mesh size(s)   M
*			of Freeform.				             M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormMeshSize                                                         M
*****************************************************************************/
IPObjectStruct *FreeFormMeshSize(IPObjectStruct *FreeForm)
{
    int i = 0;
    IPObjectStruct
	*RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    if (IP_IS_CRV_OBJ(FreeForm))
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Crvs -> Length));
    else if (IP_IS_SRF_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Srfs -> ULength));
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Srfs -> VLength));
    }
    else if (IP_IS_TRIVAR_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Trivars -> ULength));
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Trivars -> VLength));
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.Trivars -> WLength));
    }
    else if (IP_IS_TRIMSRF_OBJ(FreeForm)) {
        ListObjectInsert(RetVal, i++,
		 GenNUMValObject(FreeForm -> U.TrimSrfs -> Srf -> ULength));
        ListObjectInsert(RetVal, i++,
		 GenNUMValObject(FreeForm -> U.TrimSrfs -> Srf -> VLength));
    }
    else if (IP_IS_TRISRF_OBJ(FreeForm))
        ListObjectInsert(RetVal, i++,
			 GenNUMValObject(FreeForm -> U.TriSrfs -> Length));
    else {
	IritFatalError("FFMSize: Invalid freeform to extract length(s) from!");
	IPFreeObject(RetVal);
	return NULL;
    }

    ListObjectInsert(RetVal, i++, NULL);

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Returns on list object created out of the given knot vector sequence.    M
*                                                                            *
* PARAMETERS:                                                                M
*   KV:   	To knot vector.						     M
*   KVLen:	The length of KV					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with the knot vector.                   M
*****************************************************************************/
static IPObjectStruct *KnotVectorToListObj(CagdRType *KV, int KVLen)
{
    int i;
    IPObjectStruct
	*RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (i = 0; i < KVLen; i++)
        ListObjectInsert(RetVal, i, GenNUMValObject(KV[i]));

    ListObjectInsert(RetVal, i++, NULL);

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Returns the knot vector(s) of a given freeform.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForm:   To return its knot vector(s).				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with the knot vector(s) of Freeform.    M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormKnotVector                                                       M
*****************************************************************************/
IPObjectStruct *FreeFormKnotVector(IPObjectStruct *FreeForm)
{
    int i = 0;
    IPObjectStruct
	*RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    if (IP_IS_CRV_OBJ(FreeForm) && CAGD_IS_BSPLINE_CRV(FreeForm -> U.Crvs))
        ListObjectInsert(RetVal, i++,
		KnotVectorToListObj(FreeForm -> U.Crvs -> KnotVector,
				    FreeForm -> U.Crvs -> Order +
				      CAGD_CRV_PT_LST_LEN(FreeForm -> U.Crvs)));
    else if (IP_IS_SRF_OBJ(FreeForm) &&
	     CAGD_IS_BSPLINE_SRF(FreeForm -> U.Srfs)) {
        ListObjectInsert(RetVal, i++,
		KnotVectorToListObj(FreeForm -> U.Srfs -> UKnotVector,
				    FreeForm -> U.Srfs -> UOrder +
				      CAGD_SRF_UPT_LST_LEN(FreeForm -> U.Srfs)));
        ListObjectInsert(RetVal, i++,
		KnotVectorToListObj(FreeForm -> U.Srfs -> VKnotVector,
				    FreeForm -> U.Srfs -> VOrder +
				      CAGD_SRF_VPT_LST_LEN(FreeForm -> U.Srfs)));
    }
    else if (IP_IS_TRIVAR_OBJ(FreeForm) &&
	     TRIV_IS_BSPLINE_TV(FreeForm -> U.Trivars)) {
        ListObjectInsert(RetVal, i++,
		KnotVectorToListObj(FreeForm -> U.Trivars -> UKnotVector,
				    FreeForm -> U.Trivars -> UOrder +
				      TRIV_TV_UPT_LST_LEN(FreeForm -> U.Trivars)));
        ListObjectInsert(RetVal, i++,
		 KnotVectorToListObj(FreeForm -> U.Trivars -> VKnotVector,
				     FreeForm -> U.Trivars -> VOrder +
				      TRIV_TV_VPT_LST_LEN(FreeForm -> U.Trivars)));
        ListObjectInsert(RetVal, i++,
		 KnotVectorToListObj(FreeForm -> U.Trivars -> WKnotVector,
				     FreeForm -> U.Trivars -> WOrder +
				      TRIV_TV_WPT_LST_LEN(FreeForm -> U.Trivars)));
    }
    else if (IP_IS_TRIMSRF_OBJ(FreeForm) &&
	     CAGD_IS_BSPLINE_SRF(FreeForm -> U.TrimSrfs -> Srf)) {
        ListObjectInsert(RetVal, i++,
	    KnotVectorToListObj(FreeForm -> U.TrimSrfs -> Srf -> UKnotVector,
				FreeForm -> U.TrimSrfs -> Srf -> UOrder +
				  CAGD_SRF_UPT_LST_LEN(FreeForm -> U.TrimSrfs
						                      -> Srf)));
        ListObjectInsert(RetVal, i++,
	    KnotVectorToListObj(FreeForm -> U.TrimSrfs -> Srf -> VKnotVector,
				FreeForm -> U.TrimSrfs -> Srf -> VOrder +
				  CAGD_SRF_VPT_LST_LEN(FreeForm -> U.TrimSrfs
						                      -> Srf)));
    }
    else if (IP_IS_TRISRF_OBJ(FreeForm) &&
	TRNG_IS_BSPLINE_TRISRF(FreeForm -> U.TriSrfs)) {
        ListObjectInsert(RetVal, i++,
		KnotVectorToListObj(FreeForm -> U.TriSrfs -> KnotVector,
				    FreeForm -> U.TriSrfs -> Order +
				        FreeForm -> U.TriSrfs -> Length));
    }
    else {
	IritFatalError("FFKntVec: Invalid freeform to extract knot vector(s) from!");
	IPFreeObject(RetVal);
	return NULL;
    }

    ListObjectInsert(RetVal, i++, NULL);

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Returns the control points of a given freeform.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForm:   To return its control points.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with the control points of Freeform.    M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeFormControlPoints                                                    M
*****************************************************************************/
IPObjectStruct *FreeFormControlPoints(IPObjectStruct *FreeForm)
{
    int i, Length;
    CagdPointType PType;
    IPObjectStruct
	*RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
    CagdRType **Points;

    if (IP_IS_CRV_OBJ(FreeForm)) {
	PType = FreeForm -> U.Crvs -> PType;
	Points = FreeForm -> U.Crvs -> Points;
	Length = FreeForm -> U.Crvs -> Length;
    }
    else if (IP_IS_SRF_OBJ(FreeForm)) {
	PType = FreeForm -> U.Srfs -> PType;
	Points = FreeForm -> U.Srfs -> Points;
	Length = FreeForm -> U.Srfs -> ULength * FreeForm -> U.Srfs -> VLength;
    }
    else if (IP_IS_TRIVAR_OBJ(FreeForm)) {
	PType = FreeForm -> U.Trivars -> PType;
	Points = FreeForm -> U.Trivars -> Points;
	Length = FreeForm -> U.Trivars -> ULength *
	         FreeForm -> U.Trivars -> VLength *
	         FreeForm -> U.Trivars -> WLength;
    }
    else if (IP_IS_TRIMSRF_OBJ(FreeForm)) {
	PType = FreeForm -> U.TrimSrfs -> Srf -> PType;
	Points = FreeForm -> U.TrimSrfs -> Srf -> Points;
	Length = FreeForm -> U.TrimSrfs -> Srf -> ULength *
	         FreeForm -> U.TrimSrfs -> Srf -> VLength;
    }
    else if (IP_IS_TRISRF_OBJ(FreeForm)) {
	PType = FreeForm -> U.TriSrfs -> PType;
	Points = FreeForm -> U.TriSrfs -> Points;
	Length = TRNG_TRISRF_MESH_SIZE(FreeForm -> U.TriSrfs);
    }
    else {
	IritFatalError("FFKntVc: Invalid freeform to extract control points from!");
	IPFreeObject(RetVal);
	return NULL;
    }

    /* Copy the points themslves into the object list. */
    for (i = 0; i < Length; i++) {
	int j;
	CagdRType Coords[CAGD_MAX_PT_SIZE];

	if (CAGD_IS_RATIONAL_PT(PType))
	    Coords[0] = Points[0][i];
	for (j = 1; j <= CAGD_NUM_OF_PT_COORD(PType); j++)
	    Coords[j] = Points[j][i];
	
        ListObjectInsert(RetVal, i, GenCTLPTObject(PType, Coords, NULL));
    }

    ListObjectInsert(RetVal, i++, NULL);

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes the convex hull of the given data set, which can be either      M
* curves or polygon/polyline/points.                                         M
*                                                                            *
* PARAMETERS:                                                                M
*   Geom:        A curve or a polygon/line/points.                           M
*   FineNess:    If curve is given, the accuracy of the numeric computation. M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    An object representing the convex hull of Geom.     M
*                                                                            *
* SEE ALSO:                                                                  M
*   CrvTwoTangents, CrvPointTangents                                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   ComputeConvexHull, convex hull                                           M
*****************************************************************************/
IPObjectStruct *ComputeConvexHull(IPObjectStruct *Geom, RealType *FineNess)
{
    if (IP_IS_CRV_OBJ(Geom)) {
	CagdCrvStruct
	   *CHCrv = SymbCrvCnvxHull(Geom -> U.Crvs, *FineNess);

	return GenCRVObject(CHCrv);
    }
    else if (IP_IS_POLY_OBJ(Geom)) {
	int i,
	    Circular = FALSE;
	IPVertexStruct
	    *V = Geom -> U.Pl -> PVertex;
	int Len = IritPrsrVrtxListLen(V);
	ConvexHullPoint
	    *DTPts = (ConvexHullPoint *)
				IritMalloc(sizeof(ConvexHullPoint) * Len);

	for (i = 0; i < Len; i++, V = V -> Pnext) {
	    DTPts[i].Pt[0] = V -> Coord[0];
	    DTPts[i].Pt[1] = V -> Coord[1];
	}

	if (!ConvexHull(DTPts, &Len)) {
	    IritFree((VoidPtr) DTPts);
	    return NULL;
	}

	/* Make an object of same type: point list/polygon/polyline. */
	Geom = CopyObject(NULL, Geom, TRUE);

	/* Copy the convex hull set into it. */
	V = Geom -> U.Pl -> PVertex;
	if (IP_IS_POLYGON_OBJ(Geom)) {
	    IPVertexStruct
	        *VLast  = IritPrsrGetLastVrtx(V);

	    Circular = (VLast -> Pnext != NULL);
	    VLast -> Pnext = NULL;
	}

	for (i = 0; i < Len && V -> Pnext != NULL; ) {
	    static PointType
		ZNorm = { 0.0, 0.0, 1.0 };

	    V -> Coord[0] = DTPts[i].Pt[0];
	    V -> Coord[1] = DTPts[i].Pt[1];
	    V -> Coord[2] = 0;

	    PT_COPY(V -> Normal, ZNorm);

	    if (++i < Len && V -> Pnext != NULL)
	        V = V -> Pnext;
	}

	IritFree((VoidPtr) DTPts);
	IPFreeVertexList(V -> Pnext);
	if (Circular)
	    V -> Pnext = Geom -> U.Pl -> PVertex;
	else
	    V -> Pnext = NULL;

	return Geom;
    }
    else
	IritFatalError("CNVXHULL: Invalid type of input geometry!");

    return NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes all the lines through Pt that are also tangent to the given     M
* curve Crv.                                                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   Crv:        To compute all tangent lines to it through Pt.               M
*   TanPt:      Where all tangent lines should go throug.                    M
*   FineNess:   The accuracy of the numeric computation.                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with all parameter values of tangents.  M
*                                                                            *
* SEE ALSO:                                                                  M
*   ComputeConvexHull, CrvTwoTangents                                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvPointTangents                                                         M
*****************************************************************************/
IPObjectStruct *CrvPointTangents(IPObjectStruct *Crv,
				 PointType TanPt,
				 RealType *FineNess)
{
    int Count = 0;
    CagdPtStruct *Pt,
        *Pts = SymbCrvPtTangents(Crv -> U.Crvs, TanPt, *FineNess);
    IPObjectStruct
        *RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (Pt = Pts; Pt != NULL; Pt = Pt -> Pnext)
        ListObjectInsert(RetVal, Count++, GenNUMObject(&Pt -> Pt[0]));
    ListObjectInsert(RetVal, Count, NULL);

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes all lines that are tangent to Crv at TWO different locations    M
* on Crv.                                                                    M
*                                                                            *
* PARAMETERS:                                                                M
*   Crv:         Curve to compute mutual tangent lines.                      M
*   FineNess:    The accuracy of the numeric computation.                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object with all parameter value pairs of the   M
*		       tangents.  Each point object in list will contain     M
*		       the pair of parameter values at the X and Y	     M
*		       coordinates.					     M
*                                                                            *
* SEE ALSO:                                                                  M
*   ComputeConvexHull, CrvPointTangents                                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvTwoTangents                                                           M
*****************************************************************************/
IPObjectStruct *CrvTwoTangents(IPObjectStruct *Crv, RealType *FineNess)
{
    int Count = 0;
    RealType
	R = 0.0;
    CagdPtStruct *Pt,
        *Pts = SymbTangentToCrvAtTwoPts(Crv -> U.Crvs, *FineNess);
    IPObjectStruct
        *RetVal = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (Pt = Pts; Pt != NULL; Pt = Pt -> Pnext)
        ListObjectInsert(RetVal, Count++,
			 GenPTObject(&Pt -> Pt[0], &Pt -> Pt[1], &R));
    ListObjectInsert(RetVal, Count, NULL);

    return RetVal;
}
