/*****************************************************************************
*   "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 "allocate.h"
#include "attribut.h"
#include "objects.h"
#include "primitiv.h"
#include "windows.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);

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Construct a trimmed surfcae, 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)) {
		IritPrsrFatalError("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);
	IritPrsrFatalError(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);
	IritPrsrFatalError(Line);
	return NULL;
    }

    if (!IP_IS_OLST_OBJ(KntObjList) || ListObjectLength(KntObjList) != 3) {
	IPFreeObject(TrivObj);
	IritPrsrFatalError("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);
	IritPrsrFatalError("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);
	IritPrsrFatalError(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);
	    IritPrsrFatalError("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);
	    IritPrsrFatalError("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);
	    IritPrsrFatalError("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)
{
    int Dir = 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)
{
    int Dir = 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)
{
    int Dir = 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),
	Dir = 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);
	IritPrsrFatalError(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
*   SrfObj:    Trivar to differentiate.                                      M
*   Dir:       Direction of differentiation. Either U or V.                  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, 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:
	    IritPrsrFatalError("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, *PrevSrfObj, *HeadSrfObj;
    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)) {
	    IritFatalError("TFROMSRF: List contains non surface object(s).");
	    return NULL;
	}
	if (SrfObj -> U.Srfs -> Pnext != NULL) {
	    IritFatalError("TFROMSRF: nested surface lists are disallowed.");
	    return NULL;
	}
	NumSrfs++;
    }

    /* Chain all surfaces into a single list and invoke the TV constructor: */
    HeadSrfObj = PrevSrfObj = ListObjectGet(SrfList, 0);
    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) {
	IritFatalError("FFMerge: Only curves or surfaces!");
	return NULL;
    }

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

    if (CAGD_IS_RATIONAL_PT(PType))
        i--;
    if (i != CAGD_NUM_OF_PT_COORD(PType)) {
	IritFatalError("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
	IritFatalError("FFPtType: Invalid  freeform to extract point type from!");

    return GenNUMObject(&R);
}
