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

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

/*****************************************************************************
* DESCRIPTION:                                                               M
* Editing of a single control point of a curve.			     	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjCrv:   Curve to edit one of its control points.                      M
*   Pt:        Ptoint to replace PObjCrv's Index point.                      M
*   Index:     Index of point to replace in PObjCrv's control polygon.       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A new curve after the replacement.                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   EditCrvControlPoint                                                      M
*****************************************************************************/
IPObjectStruct *EditCrvControlPoint(IPObjectStruct *PObjCrv,
				    CagdCtlPtStruct *Pt,
				    RealType *Index)
{
    IPObjectStruct *CrvObj;
    CagdCrvStruct *Crv;

    Crv = CagdEditSingleCrvPt(PObjCrv -> U.Crvs, Pt,
			      REAL_PTR_TO_INT(Index), TRUE);

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Editing of a single control point of a surface.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjSrf:        Surface to edit one of its control points.               M
*   Pt:             Ptoint to replace PObjSrf's U/VIndex point.              M
*   UIndex, VIndex: Indices of point to replace in PObjCrv's control mesh.   M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A new surface after the replacement.                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   EditSrfControlPoint                                                      M
*****************************************************************************/
IPObjectStruct *EditSrfControlPoint(IPObjectStruct *PObjSrf,
				    CagdCtlPtStruct *Pt,
				    RealType *UIndex,
				    RealType *VIndex)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct *Srf;

    Srf = CagdEditSingleSrfPt(PObjSrf -> U.Srfs, Pt,
			      REAL_PTR_TO_INT(UIndex),
			      REAL_PTR_TO_INT(VIndex), TRUE);

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Raises the degree of the given curve.                                      M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjCrv:     Curve to raise its degree.                                  M
*   RNewOrder:   New degree of curve.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  Curve identical to PObjCrv, but with RNewOrder Order. M
*                                                                            *
* KEYWORDS:                                                                  M
*   RaiseCurveObject                                                         M
*****************************************************************************/
IPObjectStruct *RaiseCurveObject(IPObjectStruct *PObjCrv, RealType *RNewOrder)
{
    IPObjectStruct *CrvObj;
    CagdCrvStruct
	*Crv = PObjCrv -> U.Crvs;
    int OldOrder = Crv -> Order,
	NewOrder = REAL_PTR_TO_INT(RNewOrder);

    if (NewOrder <= OldOrder) {
	IritPrsrFatalError("Order to raise less the current");
	return NULL;
    }

    Crv = CagdCrvDegreeRaiseN(Crv, NewOrder);

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Raises the degree of the given surface.                                    M
*                                                                            M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjSrf:     Surface to raise its degree.                                M
*   RDir:        Direction to raise degre. Either U or V.                    M
*   RNewOrder:   New degree of surface.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  Surface identical tor PObjSrf, but with RNewOrder     M
*		       Order in direction RDir.				     M
*                                                                            *
* KEYWORDS:                                                                  M
*   RaiseSurfaceObject                                                       M
*                                                                            M
*****************************************************************************/
IPObjectStruct *RaiseSurfaceObject(IPObjectStruct *PObjSrf,
				   RealType *RDir,
				   RealType *RNewOrder)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct *TSrf,
	*Srf = PObjSrf -> U.Srfs;
    int Dir = REAL_PTR_TO_INT(RDir),
	OldOrder = Dir == CAGD_CONST_U_DIR ? Srf -> VOrder : Srf -> UOrder,
	NewOrder = REAL_PTR_TO_INT(RNewOrder);

    if (NewOrder <= OldOrder) {
	WndwInputWindowPutStr("Order to raise less the current");
	return NULL;
    }

    while (OldOrder++ < NewOrder) {
	TSrf = CagdSrfDegreeRaise(Srf, Dir);
	if (Srf != PObjSrf -> U.Srfs)
	    CagdSrfFree(Srf);
	Srf = TSrf;
    }

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Make, in place, the following two curves or surfaces compatible.           M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2:  Two curve or two surfaces to make compatible in order,    M
*		   point type and continuity.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MakeFreeFormCompatible                                                   M
*****************************************************************************/
void MakeFreeFormCompatible(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    if (IP_IS_CRV_OBJ(PObj1) && IP_IS_CRV_OBJ(PObj2)) {
        if (!CagdMakeCrvsCompatible(&PObj1 -> U.Crvs,
				    &PObj2 -> U.Crvs, TRUE, TRUE))
	    WndwInputWindowPutStr("Failed in making curves compatible.");
    }
    else if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	if (!CagdMakeSrfsCompatible(&PObj1 -> U.Srfs,
				    &PObj2 -> U.Srfs,
				    TRUE, TRUE, TRUE, TRUE))
	    WndwInputWindowPutStr("Failed in making surfaces compatible.");
    }
    else {
	WndwInputWindowPutStr("Only two surfaces or two curves expected.");
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new curve, which is the blend of the given two compatible       M
* curves.                                                                    M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjCrv1, PObjCrv2:  Two curves to blend.                                M
*   Method:              Method of blending:                                 M
*			 0 - simple linear interpolation between PObjCrv?.   M
*			 1/3 - corner cutting morphing technique.	     M
*			       scale to same length all time.		     M
*			       If 3, also filters out tangency test.	     M
*                        2/4 - same as 1 but scale to same bbox all time.    M
*			       If 4, also filters out tangency test.	     M
*                        5 - multires morphing.				     M
*   Blend:               Parameter of blend. Usually between zero and one    M
*			 form methods 0 and 3, distance between adjacent     M
*			 morphed curves from method 1 and 2.		     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Single blended curve if Method = 0, List of curves  M
*			 forming the entire corner cutting morphing if       M
*			 Method = 1.					     M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoCrvsMorphing                                                          M
*****************************************************************************/
IPObjectStruct *TwoCrvsMorphing(IPObjectStruct *PObjCrv1,
				IPObjectStruct *PObjCrv2,
				RealType *Method,
				RealType *Blend)
{
    IPObjectStruct *CrvObj;
    CagdCrvStruct
        *Crv = NULL;

    switch (REAL_PTR_TO_INT(Method)) {
	case 0:
	    Crv = SymbTwoCrvsMorphing(PObjCrv1 -> U.Crvs, PObjCrv2 -> U.Crvs,
								       *Blend);
	    break;
	case 1:
	    Crv = SymbTwoCrvsMorphingCornerCut(PObjCrv1 -> U.Crvs,
					       PObjCrv2 -> U.Crvs,
					       *Blend, FALSE, FALSE);
	    break;
	case 2:
	    Crv = SymbTwoCrvsMorphingCornerCut(PObjCrv1 -> U.Crvs,
					       PObjCrv2 -> U.Crvs,
					       *Blend, TRUE, FALSE);
	    break;
	case 3:
	    Crv = SymbTwoCrvsMorphingCornerCut(PObjCrv1 -> U.Crvs,
					       PObjCrv2 -> U.Crvs,
					       *Blend, FALSE, TRUE);
	    break;
	case 4:
	    Crv = SymbTwoCrvsMorphingCornerCut(PObjCrv1 -> U.Crvs,
					       PObjCrv2 -> U.Crvs,
					       *Blend, TRUE, TRUE);
	    break;
	case 5:
	    if (CAGD_IS_BEZIER_CRV(PObjCrv1 -> U.Crvs)) {
		CagdCrvStruct
		    *Crv1 = CnvrtBezier2BsplineCrv(PObjCrv1 -> U.Crvs),
		    *Crv2 = CnvrtBezier2BsplineCrv(PObjCrv2 -> U.Crvs);

		Crv = SymbTwoCrvsMorphingMultiRes(Crv1, Crv2, *Blend);
		CagdCrvFree(Crv1);
		CagdCrvFree(Crv2);
	    }
	    else
		Crv = SymbTwoCrvsMorphingMultiRes(PObjCrv1 -> U.Crvs,
						  PObjCrv2 -> U.Crvs,
						  *Blend);
	    break;
	default:
	    WndwInputWindowPutStr("Wrong curve morphing method.\n");
	    return NULL;
    }

    if (Crv == NULL) {
	WndwInputWindowPutStr("Curve are incompatible, use FFCOMPAT first.");
	return NULL;
    }

    if (Crv -> Pnext != NULL) {
	int i;
	IPObjectStruct *PObj;

	CrvObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
	for (i = 0; Crv != NULL; Crv = Crv -> Pnext, i++)
	    ListObjectInsert(CrvObj, i, GenCRVObject(Crv));
	ListObjectInsert(CrvObj, i, NULL);
	for (i = 0; (PObj = ListObjectGet(CrvObj, i)) != NULL; i++)
	    PObj -> U.Crvs -> Pnext = NULL;
    }
    else
	CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new surface, which is the blend of the given two compatible     M
* surfaces.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjCrv1, PObjCrv2:  Two surfaces to blend.                              M
*   Blend:               Parameter of blend. Usually between zero and one.   M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Blended surface.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoSrfsMorphing                                                          M
*****************************************************************************/
IPObjectStruct *TwoSrfsMorphing(IPObjectStruct *PObjSrf1,
				IPObjectStruct *PObjSrf2,
				RealType *Blend)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*Srf = SymbTwoSrfsMorphing(PObjSrf1 -> U.Srfs, PObjSrf2 -> U.Srfs,
								       *Blend);

    if (Srf == NULL) {
	WndwInputWindowPutStr("Surfaces are incompatible, use FFCOMPAT first.");
	return NULL;
    }
    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Converts a Bezier curve or surface into a Bspline curve or surface.        M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Bezier geometry to convert to Bspline geometry.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Same geometry as PObj but as Bspline.                M
*                                                                            *
* KEYWORDS:                                                                  M
*   CnvrtBezierToBspline                                                     M
*****************************************************************************/
IPObjectStruct *CnvrtBezierToBspline(IPObjectStruct *PObj)
{
    if (PObj -> ObjType == IP_OBJ_SURFACE) {
	CagdSrfStruct *Srf = CnvrtBezier2BsplineSrf(PObj -> U.Srfs);

	return GenSRFObject(Srf);
    }
    else if (PObj -> ObjType == IP_OBJ_CURVE) {
	CagdCrvStruct *Crv = CnvrtBezier2BsplineCrv(PObj -> U.Crvs);

	return GenCRVObject(Crv);
    }
    else {
        IritFatalError("Only surface/curve expected.");
        return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Convert a Bspline curve or surface into list of Bezier curves or surfaces. M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     A Bspline geometry to convert to a Bezier geometry.            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A Bezier geometry representing same geometry as PObj. M
*                                                                            *
* KEYWORDS:                                                                  M
*   CnvrtBsplineToBezier                                                     M
*****************************************************************************/
IPObjectStruct *CnvrtBsplineToBezier(IPObjectStruct *PObj)
{
    if (PObj -> ObjType == IP_OBJ_SURFACE) {
	CagdSrfStruct *TSrf,
	    *Srf = CnvrtBspline2BezierSrf(PObj -> U.Srfs);

	if (Srf -> Pnext == NULL)
	    return GenSRFObject(Srf);
	else {
	    int i;
	    IPObjectStruct
		*PObjList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

	    for (i = 0; Srf != NULL; i++) {
	        ListObjectInsert(PObjList, i, GenSRFObject(Srf));
		AttrSetObjectColor(ListObjectGet(PObjList, i),
				   AttrGetObjectColor(PObj));

		TSrf = Srf -> Pnext;
		Srf -> Pnext = NULL;
		Srf = TSrf;
	    }
	    ListObjectInsert(PObjList, i, NULL);

	    return PObjList;
	}				   
    }
    else if (PObj -> ObjType == IP_OBJ_CURVE) {
	CagdCrvStruct *TCrv,
	    *Crv = CnvrtBspline2BezierCrv(PObj -> U.Crvs);

	if (Crv -> Pnext == NULL)
	    return GenCRVObject(Crv);
	else {
	    int i;
	    IPObjectStruct
		*PObjList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

	    for (i = 0; Crv != NULL; i++) {
	        ListObjectInsert(PObjList, i, GenCRVObject(Crv));
		AttrSetObjectColor(ListObjectGet(PObjList, i),
				   AttrGetObjectColor(PObj));

		TCrv = Crv -> Pnext;
		Crv -> Pnext = NULL;
		Crv = TCrv;
	    }
	    ListObjectInsert(PObjList, i, NULL);

	    return PObjList;
	}				   
    }
    else {
        IritFatalError("Only surface/curve expected.");
        return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new curve/surface that is the result of the product of the      M
* given two curves or surfaces.                                              M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2: Two curves or surfaces to multiply.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Product result.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoCrvsSrfsProduct                                                       M
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsProduct(IPObjectStruct *PObj1,
				   IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = SymbSrfMult(PObj1 -> U.Srfs, PObj2 -> U.Srfs);

	Obj = Srf ? GenSRFObject(Srf) : NULL;
    }
    else if (IP_IS_CRV_OBJ(PObj1) && IP_IS_CRV_OBJ(PObj2)) {
	CagdCrvStruct
	    *Crv = SymbCrvMult(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else {
	IritFatalError("Pair of curves or pair of surfaces expected.");
    }

    return Obj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new scalar curve/surface that is the result of the dot product  M
* of the given two curves or surfaces.                                       M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2: Two curves or surfaces to multiply.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Product result.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoCrvsSrfsDotProduct                                                    M
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsDotProduct(IPObjectStruct *PObj1,
				      IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = SymbSrfDotProd(PObj1 -> U.Srfs, PObj2 -> U.Srfs);

	Obj = Srf ? GenSRFObject(Srf) : NULL;
    }
    else if (IP_IS_CRV_OBJ(PObj1) && IP_IS_CRV_OBJ(PObj2)) {
	CagdCrvStruct
	    *Crv = SymbCrvDotProd(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else {
	IritFatalError("Pair of curves or pair of surfaces expected.");
    }

    return Obj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new curve/surface that is the result of the cross product       M
* of the given two curves or surfaces.           	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2: Two curves or surfaces to multiply.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Product result.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoCrvsSrfsCrossProduct                                                  M
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsCrossProduct(IPObjectStruct *PObj1,
					IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = SymbSrfCrossProd(PObj1 -> U.Srfs, PObj2 -> U.Srfs);

	Obj = Srf ? GenSRFObject(Srf) : NULL;
    }
    else if (IP_IS_CRV_OBJ(PObj1) && IP_IS_CRV_OBJ(PObj2)) {
	CagdCrvStruct
	    *Crv = SymbCrvCrossProd(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else {
	IritFatalError("Pair of curves or pair of surfaces expected.");
    }

    return Obj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new curve/surface that is the result of the sum of the given    M
* two curves or surfaces.              			                     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2: Two curves or surfaces to add up.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Summation result.                                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoCrvsSrfsSum                                                           M
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsSum(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = SymbSrfAdd(PObj1 -> U.Srfs, PObj2 -> U.Srfs);

	Obj = Srf ? GenSRFObject(Srf) : NULL;
    }
    else if (IP_IS_CRV_OBJ(PObj1) && IP_IS_CRV_OBJ(PObj2)) {
	CagdCrvStruct
	    *Crv = SymbCrvAdd(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else {
	IritFatalError("Pair of curves or pair of surfaces expected.");
    }

    return Obj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a new curve/surface that is the result of the difference of the   M
* given two curves or surfaces.              			             M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2: Two curves or surfaces to subtract.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Subtraction result.                                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   TwoCrvsSrfsDiff                                                          M
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsDiff(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = SymbSrfSub(PObj1 -> U.Srfs, PObj2 -> U.Srfs);

	Obj = Srf ? GenSRFObject(Srf) : NULL;
    }
    else if (IP_IS_CRV_OBJ(PObj1) && IP_IS_CRV_OBJ(PObj2)) {
	CagdCrvStruct
	    *Crv = SymbCrvSub(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else {
	IritFatalError("Pair of curves or pair of surfaces expected.");
    }

    return Obj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Compute the inflection points of a curve.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Curve to compute inflection points for.                       M
*   Eps:       Accuracy of computation.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  Either inflection locations if Eps > 0, or	     M
*                      curvature field sign curve if Eps < 0.                M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvInflectionPts                                                         M
*****************************************************************************/
IPObjectStruct *CrvInflectionPts(IPObjectStruct *PObj, RealType *Eps)
{
    IPObjectStruct *NewPObj;

    if (*Eps <= 0.0) {
	CagdRType TMin, TMax;
	CagdCrvStruct *InflectCrv2D,
	    *InflectCrv = SymbCrv2DCurvatureSign(PObj -> U.Crvs);

	CagdCrvDomain(PObj -> U.Crvs, &TMin, &TMax);
	InflectCrv2D = SymbPrmtSclrCrvTo2D(InflectCrv, TMin, TMax);
	CagdCrvFree(InflectCrv);

	NewPObj = GenCRVObject(InflectCrv2D);
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = SymbCrv2DInflectionPts(PObj -> U.Crvs, *Eps);

	NewPObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

	for (IPtsTmp = IPts, i = 0;
	     IPtsTmp != NULL;
	     IPtsTmp = IPtsTmp -> Pnext, i++) {
	    ListObjectInsert(NewPObj, i, GenNUMValObject(IPtsTmp -> Pt[0]));
	}

	CagdPtFreeList(IPts);

	ListObjectInsert(NewPObj, i, NULL);
    }

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Compute the zero points of a curve.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Curve to compute inflection points for.                       M
*   Eps:       Accuracy of computation.                                      M
*   Axis:      Of search for zeros.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  The zero set of curve PObj in axis Axis.		     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvZeros		                                                     M
*****************************************************************************/
IPObjectStruct *CrvZeros(IPObjectStruct *PObj, RealType *Eps, RealType *Axis)
{
    int i;
    CagdPtStruct *ZPtsTmp,
	*ZPts = SymbCrvZeroSet(PObj -> U.Crvs, (int) *Axis, *Eps);
    IPObjectStruct
	*PObjList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (ZPtsTmp = ZPts, i = 0;
	 ZPtsTmp != NULL;
	 ZPtsTmp = ZPtsTmp -> Pnext, i++) {
	ListObjectInsert(PObjList, i, GenNUMValObject(ZPtsTmp -> Pt[0]));
    }

    CagdPtFreeList(ZPts);

    ListObjectInsert(PObjList, i, NULL);

    return PObjList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the extreme points of a curve.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     Curve to compute extremum locations for.                       M
*   Eps:      Accuracy control.                                              M
*   Axis:     Of search for extremum points.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  The extremum set of curve PObj in axis Axis.	     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvExtremes                                                              M
*****************************************************************************/
IPObjectStruct *CrvExtremes(IPObjectStruct *PObj,
			    RealType *Eps,
			    RealType *Axis)
{
    int i;
    CagdPtStruct *ExPtsTmp,
	*ExPts = SymbCrvExtremSet(PObj -> U.Crvs, (int) *Axis, *Eps);
    IPObjectStruct
	*PObjList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (ExPtsTmp = ExPts, i = 0;
	 ExPtsTmp != NULL;
	 ExPtsTmp = ExPtsTmp -> Pnext, i++) {
	ListObjectInsert(PObjList, i, GenNUMValObject(ExPtsTmp -> Pt[0]));
    }

    CagdPtFreeList(ExPts);

    ListObjectInsert(PObjList, i, NULL);

    return PObjList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the intersection points of two curves.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2:  Two curves to intersect.                                  M
*   Eps:           Accuracy control.                                         M
*   SelfInter:     Do we need to handle self intersection tests.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   List of intersection locations. If SelfInter is TRUE M
*                       assume PObj1 and PObj2 are the same and search for   M
*			self intersections.				     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvCrvInter                                                              M
*****************************************************************************/
IPObjectStruct *CrvCrvInter(IPObjectStruct *PObj1,
			    IPObjectStruct *PObj2,
			    RealType *Eps,
			    RealType *SelfInter)
{
    CagdSrfStruct
	*DistSrf = SymbSrfDistCrvCrv(PObj1 -> U.Crvs, PObj2 -> U.Crvs);
    IPObjectStruct *NewPObj;

    if (*Eps <= 0.0) {
	CagdSrfStruct *DistSrf3D;
	CagdRType UMin, UMax, VMin, VMax;

	CagdSrfDomain(DistSrf, &UMin, &UMax, &VMin, &VMax);
	DistSrf3D = SymbPrmtSclrSrfTo3D(DistSrf, UMin, UMax, VMin, VMax);
	CagdSrfFree(DistSrf);

	NewPObj = GenSRFObject(DistSrf3D);
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = SymbSrfDistFindPoints(DistSrf, *Eps,
					  REAL_PTR_TO_INT(SelfInter));

	CagdSrfFree(DistSrf);

	NewPObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

	for (IPtsTmp = IPts, i = 0;
	     IPtsTmp != NULL;
	     IPtsTmp = IPtsTmp -> Pnext, i++) {
	    ListObjectInsert(NewPObj, i, GenPTObject(&IPtsTmp -> Pt[0],
						     &IPtsTmp -> Pt[1],
						     &IPtsTmp -> Pt[2]));
	}

	CagdPtFreeList(IPts);

	ListObjectInsert(NewPObj, i, NULL);
    }

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the closest/farest/distance square field from a curve to a given  M
* point.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   PCrv:     To consider its distace to Points.                             M
*   Point:    To consider its distance to PCrv.                              M
*   MinDist:  Do we need minimal distance (TRUE) or maximal distance (FALSE).M
*   Eps:      If Eps > 0, computes the requested extremum location.          M
*	      Otherwise, computes the distance square scalar field.          M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Either extremum location or distance square field.   M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvPointDist                                                             M
*****************************************************************************/
IPObjectStruct *CrvPointDist(IPObjectStruct *PCrv,
			     PointType Point,
			     RealType *MinDist,
			     RealType *Eps)
{
    IPObjectStruct *NewPObj;

    if (*Eps > 0.0) {
	NewPObj = GenNUMValObject(SymbDistCrvPoint(PCrv -> U.Crvs,
						   Point,
						   REAL_PTR_TO_INT(MinDist),
						   *Eps));
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = SymbLclDistCrvPoint(PCrv -> U.Crvs, Point, -*Eps);

	NewPObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

	for (IPtsTmp = IPts, i = 0;
	     IPtsTmp != NULL;
	     IPtsTmp = IPtsTmp -> Pnext, i++) {
	    ListObjectInsert(NewPObj, i, GenNUMValObject(IPtsTmp -> Pt[0]));
	}

	CagdPtFreeList(IPts);

	ListObjectInsert(NewPObj, i, NULL);
    }

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the closest/farest/distance square field from a curve to a given  M
* line.									     M
*                                                                            *
* PARAMETERS:                                                                M
*   PCrv:     To consider its distace to Line.                               M
*   Point, Vec: Defining the line to consider its distance to PCrv.          M
*   MinDist:  Do we need minimal distance (TRUE) or maximal distance (FALSE).M
*   Eps:      If Eps > 0, computes the requested extremum location.          M
*	      Otherwise, computes the distance square scalar field.          M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Either extremum location or distance square field.   M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvLineDist                                                              M
*****************************************************************************/
IPObjectStruct *CrvLineDist(IPObjectStruct *PCrv,
			    PointType Point,
			    VectorType Vec,
			    RealType *MinDist,
			    RealType *Eps)
{
    IPObjectStruct *NewPObj;
    LineType Line;

    Line[0] = Vec[1];
    Line[1] = -Vec[0];
    Line[2] = -(Line[0] * Point[0] + Line[1] * Point[1]);

    if (*Eps > 0.0) {
	NewPObj = GenNUMValObject(SymbDistCrvLine(PCrv -> U.Crvs,
						  Line,
						  REAL_PTR_TO_INT(MinDist),
						  *Eps));
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = SymbLclDistCrvLine(PCrv -> U.Crvs, Line, -*Eps);

	NewPObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

	for (IPtsTmp = IPts, i = 0;
	     IPtsTmp != NULL;
	     IPtsTmp = IPtsTmp -> Pnext, i++) {
	    ListObjectInsert(NewPObj, i, GenNUMValObject(IPtsTmp -> Pt[0]));
	}

	CagdPtFreeList(IPts);

	ListObjectInsert(NewPObj, i, NULL);
    }

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Composes a curve on a curve or a surface.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    A curve or a surface to compose.                               M
*   PObj2:    A curve in the parametric space of PObj1.                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A composed curve of the form PObj1(Pobj2).           M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvComposition                                                           M
*****************************************************************************/
IPObjectStruct *CrvComposition(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_CRV_OBJ(PObj1)) {
	CagdCrvStruct
	    *Crv = SymbComposeCrvCrv(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else if (IP_IS_SRF_OBJ(PObj1)) {
	CagdCrvStruct
	    *Crv = SymbComposeSrfCrv(PObj1 -> U.Srfs, PObj2 -> U.Crvs);

	Obj = Crv ? GenCRVObject(Crv) : NULL;
    }
    else {
	IritFatalError("Curve or surface as first parameter only.");
    }

    return Obj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a layout (prisa) of the given set of surfaces.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Srfs:             Surface to layout flat.                                M
*   SamplesPerCurve:  Number of sample for piecewise linear curve	     M
*		      approximation.					     M
*   Epsilon:          Accuracy control.                                      M
*   Dir:              Direction of subdivision. Either U or V.               M
*   Space:            Spacing between the laid out pieces.                   M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    A list of flat 2d surfaces approximating Srfs.      M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfsPrisa                                                                M
*****************************************************************************/
IPObjectStruct *SrfsPrisa(IPObjectStruct *Srfs,
			  CagdRType *SamplesPerCurve,
			  CagdRType *Epsilon,
			  CagdRType *Dir,
			  CagdVType Space)
{
    int i;
    IPObjectStruct *PObjList, *PObj;
    CagdSrfStruct *Srf,
	*PrisaSrfs = SymbAllPrisaSrfs(Srfs -> U.Srfs,
				      REAL_PTR_TO_INT(SamplesPerCurve),
				      *Epsilon,
				      REAL_PTR_TO_INT(Dir),
				      Space);

    /* Break the linear list into a list object. */
    PObjList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
    for (Srf = PrisaSrfs, i = 0; Srf != NULL; Srf = Srf -> Pnext, i++)
	ListObjectInsert(PObjList, i, GenSRFObject(Srf));
    ListObjectInsert(PObjList, i, NULL);
    for (i = 0; (PObj = ListObjectGet(PObjList, i)) != NULL; i++)
	PObj -> U.Srfs -> Pnext = NULL;

    return PObjList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes an adaptive isocurve coverage to the given surface.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:         To compute adaptive isocurve coverage for.                  M
*   Dir:         Direction of adaptive isocurves. Either U or V.             M
*   Eps:         Coverage accuracy.                                          M
*   FullIso:     Do we want full isocurves or just trimmed ones.             M
*   SinglePath:  Do we want everying in one long curve?                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   An adaptive isocurve coverage for Srf.               M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfAdapIsoCurves                                                         M
*****************************************************************************/
IPObjectStruct *SrfAdapIsoCurves(IPObjectStruct *Srf,
				 CagdRType *Dir,
				 CagdRType *Eps,
				 CagdRType *FullIso,
				 CagdRType *SinglePath)
{
    IPObjectStruct *NewPObj;
    CagdCrvStruct
	*Crv = SymbAdapIsoExtract(Srf -> U.Srfs, NULL, NULL,
				  REAL_PTR_TO_INT(Dir),
				  *Eps, REAL_PTR_TO_INT(FullIso),
				  REAL_PTR_TO_INT(SinglePath));

    NewPObj = GenCRVObject(Crv);

    return NewPObj;
}
/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the parametric domain of the given curve or surface, trimmed       M
* surface or a trivariate.					             M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeformObj:  To return its parametric domain.                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list of two (curve) or four (surface, trimmed       M
*                      surface) or six (trivariate) numbers representing     M
*		       the parametric domain of FreeformObj.		     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetFreefromParamDomain                                                   M
*****************************************************************************/
IPObjectStruct *GetFreefromParamDomain(IPObjectStruct *FreeformObj)
{
    int i, n;
    IPObjectStruct *NewPObj;
    CagdRType Domain[6];

    if (IP_IS_CRV_OBJ(FreeformObj)) {
	CagdCrvDomain(FreeformObj -> U.Crvs, &Domain[0], &Domain[1]);
	n = 2;
    }
    else if (IP_IS_SRF_OBJ(FreeformObj)) {
	CagdSrfDomain(FreeformObj -> U.Srfs,
		      &Domain[0], &Domain[1], &Domain[2], &Domain[3]);
	n = 4;
    }
    else if (IP_IS_TRIMSRF_OBJ(FreeformObj)) {
	CagdSrfDomain(FreeformObj -> U.TrimSrfs -> Srf,
		      &Domain[0], &Domain[1], &Domain[2], &Domain[3]);
	n = 4;
    }
    else if (IP_IS_TRIVAR_OBJ(FreeformObj)) {
	TrivTVDomain(FreeformObj -> U.Trivars,
		     &Domain[0], &Domain[1],
		     &Domain[2], &Domain[3],
		     &Domain[4], &Domain[5]);
	n = 6;
    }
    else {
	IritFatalError("Curve or (trimmed) surface or trivariate as first parameter only.");
	return NULL;
    }

    NewPObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
    for (i = 0; i < n ; i++)
	ListObjectInsert(NewPObj, i, GenNUMValObject(Domain[i]));
    ListObjectInsert(NewPObj, i, NULL);

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a least square approximation/Interpolation of 1d pt data set.     M
*                                                                            *
* PARAMETERS:                                                                M
*   PtObjList:   Points to least squares fit.                                M
*   ROrder:      Order of fitting curve.                                     M
*   RCrvSize:    Size of fitting curve.                                      M
*   RParamType:  Point type.                                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A Bspline curve that least squares fits PtObjList.    M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvLeastSquarePtData                                                     M
*****************************************************************************/
IPObjectStruct *CrvLeastSquarePtData(IPObjectStruct *PtObjList,
				     CagdRType *ROrder,
				     CagdRType *RCrvSize,
				     CagdRType *RParamType)
{
    int i,
	Order = REAL_PTR_TO_INT(ROrder),
	CrvSize = REAL_PTR_TO_INT(RCrvSize);
    CagdParametrizationType
	ParamType = REAL_PTR_TO_INT(RParamType);
    CagdPtStruct
	*Pt = NULL,
	*PtList = NULL;
    CagdCrvStruct
	*Crv = NULL;
    IPObjectStruct *PtObj;

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

    for (i = 0; (PtObj = ListObjectGet(PtObjList, i)) != NULL; i++) {
	IPObjectStruct *PointObj;

	if (!IP_IS_CTLPT_OBJ(PtObj) &&
	    !IP_IS_POINT_OBJ(PtObj) &&
	    !IP_IS_VEC_OBJ(PtObj)) {
	    CagdPtFreeList(PtList);
	    IritPrsrFatalError("Non point object found in list");
	    return NULL;
	}

	PointObj = IritPrsrCoerceObjectTo(PtObj, IP_OBJ_POINT);
	if (PtList == NULL)
	    PtList = Pt = CagdPtNew();
	else {
	    Pt -> Pnext = CagdPtNew();
	    Pt = Pt -> Pnext;
	}
	PT_COPY(Pt -> Pt, PointObj -> U.Pt);
	IPFreeObject(PointObj);
    }
	
    if (i < 3 || i < Order ||
	(CrvSize != 0 && (i < CrvSize || CrvSize < Order))) {
	IritPrsrFatalError("Number of points to interpolate does not match order/length of curve.");
    }
    else {
	Crv = BspCrvInterpPts(PtList, Order, CrvSize, ParamType);
    }

    CagdPtFreeList(PtList);

    return Crv ? GenCRVObject(Crv) : NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a least square approximation/Interpolation of 2d pt data set.     M
*                                                                            *
* PARAMETERS:                                                                M
*   LstObjList:   List of point lists.                                       M
*   RUOrder:      Uorder of fitting surface.                                 M
*   RVOrder:      Vorder of fitting surface.                                 M
*   RUSize:       USize of fitting curve.                                    M
*   RVSize:       VSize of fitting curve.                                    M
*   RParamType:   Point type.                                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A Bspline surface that least square fits LstObjList. M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfLeastSquarePtData                                                     M
*****************************************************************************/
IPObjectStruct *SrfLeastSquarePtData(IPObjectStruct *LstObjList,
				     CagdRType *RUOrder,
				     CagdRType *RVOrder,
				     CagdRType *RUSize,
				     CagdRType *RVSize,
				     CagdRType *RParamType)
{
    int i, j, k,
	NumVertices = 0,
	NumLists = 0,
	NumVerticesFirst = 0,
	UOrder = REAL_PTR_TO_INT(RUOrder),
	VOrder = REAL_PTR_TO_INT(RVOrder),
	USize = REAL_PTR_TO_INT(RUSize),
	VSize = REAL_PTR_TO_INT(RVSize);
    CagdParametrizationType
	ParamType = REAL_PTR_TO_INT(RParamType);
    CagdPtStruct **PtListArray, *Pt;
    CagdSrfStruct
	*Srf = NULL;
    IPObjectStruct *LstObj, *PtObj;

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

    while ((LstObj = ListObjectGet(LstObjList, NumLists)) != NULL) {
	if (!IP_IS_OLST_OBJ(LstObj))
	    return NULL;

        NumVertices = -1;
        while ((PtObj = ListObjectGet(LstObj, ++NumVertices)) != NULL) {
	    if (!IP_IS_CTLPT_OBJ(PtObj) &&
		!IP_IS_POINT_OBJ(PtObj) &&
		!IP_IS_VEC_OBJ(PtObj)) {
		return NULL;
	    }
	}

	if (NumLists++ == 0)
	    NumVerticesFirst = NumVertices;
        else
	    if (NumVerticesFirst != NumVertices)
	        return NULL;
    }

    /* Coerce all points to a point type, in place. */
    while ((LstObj = ListObjectGet(LstObjList, NumLists)) != NULL)
	IritPrsrCoercePtsListTo(LstObj, IP_OBJ_POINT);

    PtListArray = (CagdPtStruct **)
	IritMalloc(sizeof(CagdPtStruct *) * (NumLists + 1));
    PtListArray[NumLists] = NULL;

    for (i = 0; (LstObj = ListObjectGet(LstObjList, i)) != NULL; i++) {
	Pt = PtListArray[i] = CagdPtNew();
	for (j = 0; (PtObj = ListObjectGet(LstObj, j)) != NULL; j++) {
	    for (k = 0; k < 3; k++)
		Pt -> Pt[k] = PtObj -> U.Pt[k];

	    if (j < NumVertices - 1) {
		Pt -> Pnext = CagdPtNew();
		Pt = Pt -> Pnext;
	    }
	}
    }
	
    if (NumLists < 3 ||	NumVertices < 3 ||
	NumLists < UOrder || NumVertices < VOrder ||
	(USize != 0 && (NumLists < USize || USize < UOrder)) ||
	(VSize != 0 && (NumVertices < VSize || VSize < VOrder))) {
	IritPrsrFatalError("Number of points to interpolate does not match order/length of curve.");
    }
    else {
	Srf = BspSrfInterpPts(PtListArray, UOrder, VOrder,
			      USize, VSize, ParamType);
    }

    for (i = 0; i < NumLists; i++)
	CagdPtFreeList(PtListArray[i]);
    IritFree((VoidPtr) PtListArray);

    return Srf ? GenSRFObject(Srf) : NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*  Routine to fetch the resolution parameter from the RESOLUTION object.     M
*  If ClipToMin TRUE, the Resolution is clipped to not be below MIN_RES.     M
*                                                                            *
* PARAMETERS:                                                                M
*   ClipToMin:    Do we want a lower bound?                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:          Current resolution level.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   GetResolution                                                            M
*****************************************************************************/
int GetResolution(int ClipToMin)
{
    int Resolution;
    IPObjectStruct *PObj = GetObject("RESOLUTION");

    if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
	IritPrsrFatalError("No numeric object name RESOLUTION is defined");
	Resolution = DEFAULT_RESOLUTION;
    }
    else {
	if (ClipToMin) {
	    Resolution = MAX(((int) (PObj -> U.R)), MIN_RESOLUTION);

	    Resolution = (Resolution / 2) * 2;        /* Make sure its even. */
	}
	else {
	    Resolution = (int) (PObj -> U.R);
	}
    }

    return Resolution;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a multi-resolution decomposition for a given curve using least    M
* squares. Returned is a list of curves of the decomposition.                M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:     Curve to multi-resolution decompose.                         M
*   Discont:    Do we want to preserve discontinuities?                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A list object of all the curves of the decomposition. M
*                                                                            *
* KEYWORDS:                                                                  M
*   CurveMultiResDecomp                                                      M
*****************************************************************************/
IPObjectStruct *CurveMultiResDecomp(IPObjectStruct *CrvObj, RealType *Discont)
{
    int i;
    IPObjectStruct *CrvObjList;
    SymbMultiResCrvStruct
	*MResCrv = SymbCrvMultiResDecomp(CrvObj -> U.Crvs,
					 REAL_PTR_TO_INT(Discont));

    if (MResCrv == NULL) {
        IritPrsrFatalError("Multi resolution decomposition failed.");
        return NULL;
    }

    CrvObjList = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);

    for (i = 0; i < MResCrv -> Levels; i++)
	ListObjectInsert(CrvObjList, i,
			 GenCRVObject(CagdCrvCopy(MResCrv -> HieCrv[i])));

    ListObjectInsert(CrvObjList, i, NULL);

    SymbCrvMultiResFree(MResCrv);

    return CrvObjList;
}
