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

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

static IPObjectStruct *GetControlMesh(IPObjectStruct *LstObjList,
				    int UOrder, int VOrder,
				    CagdGeomType GType, char **ErrStr);
static IPObjectStruct *GetControlPoly(IPObjectStruct *PtObjList, int Order,
				    CagdGeomType GType, char **ErrStr);
static CagdRType *GetKnotVector(IPObjectStruct *KntObjList, int Order,
			 CagdRType *KnotVector, int *Length, char **ErrStr);

/*****************************************************************************
*  Routine to fetch the boolean object DrawCtlPt.			     *
*****************************************************************************/
int GetDrawCtlPt(void)
{
    int DrawCtlPt;
    IPObjectStruct
	*PObj = GetObject("DRAWCTLPT");

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

    return DrawCtlPt;
}

/*****************************************************************************
*  Routine to fetch the boolean object DrawCtlPt.			     *
*****************************************************************************/
int GetFourPerFlat(void)
{
    int FourPerFlat;
    IPObjectStruct
	*PObj = GetObject("FLAT4PLY");

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

    return FourPerFlat;
}

/*****************************************************************************
* Routine to copy the control mesh lists to the surface control mesh.        *
*   The surface is allocated here as well.				     *
*   Returns the surface if o.k., otherwise NULL.			     *
*****************************************************************************/
static IPObjectStruct *GetControlMesh(IPObjectStruct *LstObjList,
				      int UOrder, int VOrder,
				      CagdGeomType GType, char **ErrStr)
{
    int i, j, k, PtSize,
	NumVertices = 0,
	NumVerticesFirst = -1,
        NumLists = 0;
    CagdRType **r;
    RealType *v;
    IPObjectStruct *SrfObj, *LstObj, *PtObj;
    CagdPointType
	PtType = CAGD_PT_E3_TYPE;

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

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

        NumVertices = 0;
        while ((PtObj = ListObjectGet(LstObj, NumVertices)) != NULL) {
	    if (!IP_IS_CTLPT_OBJ(PtObj)) {
		*ErrStr = "None point object found in list";
		return NULL;
	    }
	    if (NumVertices++ == 0 && NumLists == 0)	       /* First one. */
		PtType = PtObj -> U.CtlPt.PtType;
            else if (PtType != PtObj -> U.CtlPt.PtType) {
	        *ErrStr = "Different point types found in list";
	        return NULL;
            }
	}
	if (NumLists++ == 0)
	    NumVerticesFirst = NumVertices;
        else
	    if (NumVerticesFirst != NumVertices) {
	        *ErrStr = "Different size of point lists";
	        return NULL;
            }

    }

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

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

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

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

	    v = VObj -> U.CtlPt.Coords;

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

    return SrfObj;
}

/*****************************************************************************
* Routine to copy the control polygon list to the curve control polygon.     *
*   The curve is allocated here as well.				     *
*   Returns the curve if o.k., otherwise NULL.				     *
*****************************************************************************/
static IPObjectStruct *GetControlPoly(IPObjectStruct *PtObjList, int Order,
				      CagdGeomType GType, char **ErrStr)
{
    int i, j, PtSize,
        NumVertices = 0;
    CagdRType **r;
    RealType *v;
    IPObjectStruct *CrvObj, *PtObj;
    CagdPointType
	PtType = CAGD_PT_E3_TYPE;

    *ErrStr = NULL;

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

    while ((PtObj = ListObjectGet(PtObjList, NumVertices)) != NULL) {
	if (!IP_IS_CTLPT_OBJ(PtObj)) {
	    *ErrStr = "None point object found in list";
	    return NULL;
	}

        if (NumVertices++ == 0)				       /* First one. */
	    PtType = PtObj -> U.CtlPt.PtType;
        else if (PtType != PtObj -> U.CtlPt.PtType) {
	    *ErrStr = "Different point types found in list";
	    return NULL;
	}
    }

    if (NumVertices < 2) {
	*ErrStr = "Less than 2 points";
	return NULL;
    }

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

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

	v = VObj -> U.CtlPt.Coords;

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

    return CrvObj;
}

/*****************************************************************************
*   Routine to copy the list of knots into the knot vector provided.         *
* Returns KnotVector if o.k., NULL otherwise (sets ErrStr to description).   *
*   If Length == 0 it is figured from the parameters and KnotVector is       *
* allocated here.							     *
*****************************************************************************/
static CagdRType *GetKnotVector(IPObjectStruct *KntObjList, int Order,
			    CagdRType *KnotVector, int *Length, char **ErrStr)
{
    int NumKnots = 0,
	NewKnotVector = FALSE;
    IPObjectStruct *KntObj;

    *ErrStr = NULL;

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

    if (*Length == 0) {
	*Length = ListObjectLength(KntObjList);
	KnotVector = (CagdRType *) IritMalloc(sizeof(CagdRType) * *Length);
	NewKnotVector = TRUE;
    }

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

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

    if (NumKnots == 1 && KnotVector[0] < KV_MIN_LEGAL) {
	switch ((int) (KnotVector[0] - 0.5)) {
	    case KV_UNIFORM_OPEN:
		if (NewKnotVector) {
		    IritFree((VoidPtr) KnotVector);
		    KnotVector = BspKnotUniformOpen(*Length - Order, Order,
									NULL);
		}
		else
		    BspKnotUniformOpen(*Length - Order, Order, KnotVector);
		break;
	    case KV_UNIFORM_FLOAT:
		if (NewKnotVector) {
		    IritFree((VoidPtr) KnotVector);
		    KnotVector = BspKnotUniformFloat(*Length - Order, Order,
									NULL);
		}
		else
		    BspKnotUniformFloat(*Length - Order, Order, KnotVector);
		break;
	    default:
		*ErrStr = "Invalid knot value";
		fprintf(stderr,"Knot = %10.6lg (%d)",
			KnotVector[0], (int) (KnotVector[0] - 0.5));
		return NULL;
	}
    }
    else if (NumKnots != *Length) {
	*ErrStr = "Wrong knot vector length";
	return NULL;
    }

    return KnotVector;
}

/*****************************************************************************
*   Routine to create a Bezier surface geometric object defined by a list of *
* lists of vertices.							     *
*****************************************************************************/
IPObjectStruct *GenBezierSurfaceObject(IPObjectStruct *LstObjList)
{
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*SrfObj = GetControlMesh(LstObjList, -1, -1, CAGD_SBEZIER_TYPE, &ErrStr);

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

    return SrfObj;
}

/*****************************************************************************
*   Routine to create a Bezier curve geometric object defined by a list of   *
* vertices.								     *
*****************************************************************************/
IPObjectStruct *GenBezierCurveObject(IPObjectStruct *PtObjList)
{
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*CrvObj = GetControlPoly(PtObjList, -1, CAGD_CBEZIER_TYPE, &ErrStr);

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

    return CrvObj;
}


/*****************************************************************************
*   Routine to create a Bspline surface geometric object defined by a list   *
* of vertices.								     *
*****************************************************************************/
IPObjectStruct *GenBsplineSurfaceObject(RealType *RUOrder, RealType *RVOrder,
		      IPObjectStruct *LstObjList, IPObjectStruct *KntObjList)
{
    int Len1, Len2,
	UOrder = REAL_PTR_TO_INT(RUOrder),
	VOrder = REAL_PTR_TO_INT(RVOrder);
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*SrfObj = GetControlMesh(LstObjList, UOrder, VOrder,
					  CAGD_SBSPLINE_TYPE, &ErrStr);

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

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

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

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

    return SrfObj;

}

/*****************************************************************************
*   Routine to create a Bspline curve geometric object defined by a list of  *
* vertices.								     *
*****************************************************************************/
IPObjectStruct *GenBsplineCurveObject(RealType *ROrder,
			 IPObjectStruct *PtObjList, IPObjectStruct *KntObjList)
{
    int Len,
	Order = REAL_PTR_TO_INT(ROrder);
    char *ErrStr, Line[LINE_LEN];
    IPObjectStruct
	*CrvObj = GetControlPoly(PtObjList, Order, CAGD_CBSPLINE_TYPE, &ErrStr);

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

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

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

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

    return CrvObj;
}

/*****************************************************************************
* Routine to subdivide a surface into two in specified direction (1 or 2)    *
* and specified parameter value.					     *
*****************************************************************************/
IPObjectStruct *DivideSurfaceObject(IPObjectStruct *SrfObj, RealType *RDir,
							RealType *ParamVal)
{
    int Dir = REAL_PTR_TO_INT(RDir);
    CagdSrfStruct
	*Srf = CagdSrfSubdivAtParam(SrfObj -> U.Srfs, *ParamVal, Dir);
    IPObjectStruct *Srf1, *Srf2, *SrfList;

    if (Srf == NULL)
	return NULL;

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

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

    return SrfList;
}

/*****************************************************************************
* Routine to extract a surface region in specified direction (1 or 2) and    *
* specified parameter values.						     *
*****************************************************************************/
IPObjectStruct *RegionFromSurfaceObject(IPObjectStruct *SrfObj, RealType *RDir,
				      RealType *ParamVal1, RealType *ParamVal2)
{
    int Dir = REAL_PTR_TO_INT(RDir);
    CagdSrfStruct
	*Srf = CagdSrfRegionFromSrf(SrfObj -> U.Srfs,
				    *ParamVal1, *ParamVal2, Dir);

    if (Srf == NULL)
	return NULL;

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* Routine to subdivide a curve into two in specified parameter value.	     *
*****************************************************************************/
IPObjectStruct *DivideCurveObject(IPObjectStruct *CrvObj, RealType *ParamVal)
{
    CagdCrvStruct
	*Crv = CagdCrvSubdivAtParam(CrvObj -> U.Crvs, *ParamVal);
    IPObjectStruct *Crv1, *Crv2, *CrvList;

    if (Crv == NULL)
	return NULL;

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

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

    return CrvList;
}

/*****************************************************************************
* Routine to extract a curve region in specified parameter values.	     *
*****************************************************************************/
IPObjectStruct *RegionFromCurveObject(IPObjectStruct *CrvObj,
				    RealType *ParamVal1, RealType *ParamVal2)
{
    CagdCrvStruct
	*Crv = CagdCrvRegionFromCrv(CrvObj -> U.Crvs, *ParamVal1, *ParamVal2);

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* Routine to refine surface in specified direction (1 or 2) and knot vector. *
* If, however, Replace is non zero, KnotsObj REPLACES current vector.        *
*****************************************************************************/
IPObjectStruct *RefineSurfaceObject(IPObjectStruct *SrfObj, RealType *RDir,
				  RealType *RReplace, IPObjectStruct *KnotsObj)
{
    int n = 0,
	Replace = REAL_PTR_TO_INT(RReplace),
	Dir = REAL_PTR_TO_INT(RDir);
    char *ErrStr, Line[LINE_LEN];
    CagdRType
	*t = GetKnotVector(KnotsObj, 0, NULL, &n, &ErrStr);
    CagdSrfStruct *RefSrf;
    IPObjectStruct *RefSrfObj;

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

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

/*****************************************************************************
* Routine to refine curve in specified knot vector.			     *
* If, however, Replace is non zero, knotsObj REPLACES current vector.        *
*****************************************************************************/
IPObjectStruct *RefineCurveObject(IPObjectStruct *CrvObj, RealType *RReplace,
						     IPObjectStruct *KnotsObj)
{
    int n = 0,
	Replace = REAL_PTR_TO_INT(RReplace);
    char *ErrStr, Line[LINE_LEN];
    CagdRType
	*t = GetKnotVector(KnotsObj, 0, NULL, &n, &ErrStr);
    CagdCrvStruct *RefCrv;
    IPObjectStruct *RefCrvObj;

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

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

/*****************************************************************************
* Routine to evaluate surface in specified parameter values.		     *
*****************************************************************************/
IPObjectStruct *EvalSurfaceObject(IPObjectStruct *SrfObj, RealType *u, RealType *v)
{
    CagdRType
	*Pt = CagdSrfEval(SrfObj -> U.Srfs, *u, *v);
    IPObjectStruct
	*CtlPtObj = GenCTLPTObject(SrfObj -> U.Srfs -> PType, Pt, NULL);

    return CtlPtObj;
}

/*****************************************************************************
* Routine to evaluate curve in specified parameter value.		     *
*****************************************************************************/
IPObjectStruct *EvalCurveObject(IPObjectStruct *CrvObj, RealType *t)
{
    CagdRType
	*Pt = CagdCrvEval(CrvObj -> U.Crvs, *t);
    IPObjectStruct
	*CtlPtObj = GenCTLPTObject(CrvObj -> U.Crvs -> PType, Pt, NULL);

    return CtlPtObj;
}

/*****************************************************************************
* Routine to compute the erivative surface in Dir of SrfObj.		     * 
*****************************************************************************/
IPObjectStruct *DeriveSurfaceObject(IPObjectStruct *SrfObj, RealType *Dir)
{
    CagdSrfStruct
	*DerivSrf = CagdSrfDerive(SrfObj -> U.Srfs, REAL_PTR_TO_INT(Dir));
    IPObjectStruct
	*DerivSrfObj = GenSRFObject(DerivSrf);

    return DerivSrfObj;
}

/*****************************************************************************
* Routine to compute the derivative curve of CrvObj.			     *
*****************************************************************************/
IPObjectStruct *DeriveCurveObject(IPObjectStruct *CrvObj)
{
    CagdCrvStruct
	*DerivCrv = CagdCrvDerive(CrvObj -> U.Crvs);
    IPObjectStruct
	*DerivCrvObj = GenCRVObject(DerivCrv);

    return DerivCrvObj;
}

/*****************************************************************************
* Routine to compute the normal surface of SrfObj.			     *
*****************************************************************************/
IPObjectStruct *SurfaceNormalObject(IPObjectStruct *SrfObj)
{
    CagdSrfStruct
	*NormalSrf = CagdSrfNormalSrf(SrfObj -> U.Srfs);
    IPObjectStruct
	*NormalSrfObj = GenSRFObject(NormalSrf);

    return NormalSrfObj;
}

/*****************************************************************************
* Routine to evaluate surface normal in specified parameter values.	     *
*****************************************************************************/
IPObjectStruct *NormalSurfaceObject(IPObjectStruct *SrfObj, RealType *u,
								  RealType *v)
{
    int i;
    RealType V[3];
    CagdVecStruct
	*Vec = CagdSrfNormal(SrfObj -> U.Srfs, *u, *v);
    IPObjectStruct *NormalObj;

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

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

    return NormalObj;
}

/*****************************************************************************
* Routine to evaluate surface tangent in specified parameter value and dir.. *
*****************************************************************************/
IPObjectStruct *TangentSurfaceObject(IPObjectStruct *SrfObj, RealType *RDir,
						     RealType *u, RealType *v)
{
    int i,
	Dir = REAL_PTR_TO_INT(RDir);
    RealType V[3];
    CagdVecStruct
	*Vec = CagdSrfTangent(SrfObj -> U.Srfs, *u, *v, Dir);
    IPObjectStruct *TangentObj;

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

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

    return TangentObj;
}

/*****************************************************************************
* Routine to evaluate curve in specified parameter value.		     *
*****************************************************************************/
IPObjectStruct *TangentCurveObject(IPObjectStruct *CrvObj, RealType *t)
{
    int i;
    RealType V[3];
    CagdVecStruct
	*Vec = CagdCrvTangent(CrvObj -> U.Crvs, *t);
    IPObjectStruct *TangentObj;

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

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

    return TangentObj;
}

/*****************************************************************************
* Routine to extract an isoparametric curve out of a surface.		     *
*****************************************************************************/
IPObjectStruct *CurveFromSurface(IPObjectStruct *SrfObj, RealType *RDir,
							    RealType *ParamVal)
{
    int Dir = REAL_PTR_TO_INT(RDir);
    CagdCrvStruct
	*Crv = CagdCrvFromSrf(SrfObj -> U.Srfs, *ParamVal, Dir);
    IPObjectStruct *CrvObj;

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* Routine to extract an isoparametric curve out of a surface mesh.	     *
*****************************************************************************/
IPObjectStruct *CurveFromSrfMesh(IPObjectStruct *SrfObj, RealType *RDir,
							RealType *RIndex)
{
    int Dir = REAL_PTR_TO_INT(RDir),
	Index = REAL_PTR_TO_INT(RIndex);
    CagdCrvStruct
	*Crv = CagdCrvFromMesh(SrfObj -> U.Srfs, Index, Dir);
    IPObjectStruct *CrvObj;

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* Routine to reverse a curve.						     *
*****************************************************************************/
IPObjectStruct *CurveReverse(IPObjectStruct *CrvObj)
{
     CagdCrvStruct
	*RevCrv = CagdCrvReverse(CrvObj -> U.Crvs);

    if (RevCrv == NULL)
	return NULL;

    CrvObj = GenCRVObject(RevCrv);

    return CrvObj;
}

/*****************************************************************************
* Routine to reverse a surface.						     *
*****************************************************************************/
IPObjectStruct *SurfaceReverse(IPObjectStruct *SrfObj)
{
    CagdSrfStruct
	*RevSrf = CagdSrfReverse(SrfObj -> U.Srfs);

    if (RevSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(RevSrf);

    return SrfObj;
}

/*****************************************************************************
* Routine to convert curve(s) to a piecewise linear polyline approximation   *
* and convert its control polygon into polyline as well.		     *
*   Result is saved as an attribute in the object.			     *
*   If however approximation alread exists, no computation is performed.     *
*****************************************************************************/
void ComputeCurveIsoLines(IPObjectStruct *PObj)
{
    int i, j,
	DrawCtlPtColor = GetDrawCtlPt(),
	Resolution = GetResolution(TRUE),
	RealResolution = GetResolution(FALSE);
    IPPolygonStruct *Poly;
    IPObjectStruct *PObjPoly;
    CagdCrvStruct *Crv;

    if (!IP_IS_CRV_OBJ(PObj))
	IritFatalError("Curve was expected.");

    if (RealResolution > 0 &&
	AttrGetObjectObjAttrib(PObj, "_isoline") == NULL) {
        for (i = 1, j = Resolution; j > 0 && i < 15; i++, j >>= 1);
        BzrCrvSetCache(i, TRUE);

	PObjPoly = GenPOLYObject(NULL);
	PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
	IP_SET_POLYLINE_OBJ(PObjPoly);
	for (Crv = PObj -> U.Crvs; Crv != NULL; Crv = Crv -> Pnext) {
	    /* Add heuristically more samples if curve has interior knots. */
	    if (CAGD_IS_BSPLINE_CRV(Crv)) {
		int n = (1 << i) +
		    (Crv -> Length - Crv -> Order) * (Crv -> Order - 1);

		for (j = i; n >= (1 << j); j++);
		Poly = IritCurve2Polylines(Crv, j);
	    }
	    else
		Poly = IritCurve2Polylines(Crv, i);

	    Poly -> Pnext = PObjPoly -> U.Pl;
	    PObjPoly -> U.Pl = Poly;
	}
	AttrSetObjectObjAttrib(PObj, "_isoline", PObjPoly);
    }

    if (DrawCtlPtColor &&
	AttrGetObjectObjAttrib(PObj, "_ctlpoly") == NULL) {
	PObjPoly = GenPOLYObject(NULL);
	PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
	IP_SET_POLYLINE_OBJ(PObjPoly);
	for (Crv = PObj -> U.Crvs; Crv != NULL; Crv = Crv -> Pnext) {
	    Poly = IritCurve2CtlPoly(Crv);

	    Poly -> Pnext = PObjPoly -> U.Pl;
	    PObjPoly -> U.Pl = Poly;
	}
	AttrSetObjectObjAttrib(PObj, "_ctlpoly", PObjPoly);
    }
}

/*****************************************************************************
* Routine to convert a surface to a set of piecewise linear polylines	     *
* approximation and convert its control mesh into set of polyline as well.   *
*  Result is saved in the PLPolys/CtlMsh surface slots.			     *
* If however approximation alread exists, no computation is performed.       *
*****************************************************************************/
void ComputeSurfaceIsoLines(IPObjectStruct *PObj)
{
    int i, j,
	DrawCtlPtColor = GetDrawCtlPt(),
	Resolution = GetResolution(TRUE),
	RealResolution = GetResolution(FALSE);
    IPPolygonStruct *Polys, *PolyTmp;
    IPObjectStruct *PObjPolys;
    CagdSrfStruct *Srf;

    if (!IP_IS_SRF_OBJ(PObj))
	IritFatalError("Surface was expected.");

    if (RealResolution > 0 &&
	AttrGetObjectObjAttrib(PObj, "_isoline") == NULL) {
    	for (i = 1, j = Resolution; j > 0 && i < 10; i++, j >>= 1);
    	BzrCrvSetCache(i, TRUE);

	PObjPolys = GenPOLYObject(NULL);
	PObjPolys -> Attrs = AttrCopyAttributes(PObj -> Attrs);
	for (Srf = PObj -> U.Srfs; Srf != NULL; Srf = Srf -> Pnext) {
	    int NumOfIso[2];

	    NumOfIso[0] = -Resolution;
	    NumOfIso[1] = -Resolution;
	    Polys = IritSurface2Polylines(Srf, NumOfIso, i);

	    for (PolyTmp = Polys;
		 PolyTmp -> Pnext != NULL;
		 PolyTmp = PolyTmp -> Pnext);
	    PolyTmp -> Pnext = PObjPolys -> U.Pl;
	    PObjPolys -> U.Pl = Polys;
	}
	AttrSetObjectObjAttrib(PObj, "_isoline", PObjPolys);
    }
    if (DrawCtlPtColor &&
	AttrGetObjectObjAttrib(PObj, "_ctlmesh") == NULL) {
	PObjPolys = GenPOLYObject(NULL);
	PObjPolys -> Attrs = AttrCopyAttributes(PObj -> Attrs);
	AttrSetObjectColor(PObjPolys, GetDrawCtlPt());
	IP_SET_POLYLINE_OBJ(PObjPolys);
	for (Srf = PObj -> U.Srfs; Srf != NULL; Srf = Srf -> Pnext) {
	    Polys = IritSurface2CtlMesh(Srf);

	    for (PolyTmp = Polys;
		 PolyTmp -> Pnext != NULL;
		 PolyTmp = PolyTmp -> Pnext);
	    PolyTmp -> Pnext = PObjPolys -> U.Pl;
	    PObjPolys -> U.Pl = Polys;
	}
	AttrSetObjectObjAttrib(PObj, "_ctlmesh", PObjPolys);
    }
}

/*****************************************************************************
* Routine to convert a surface to a set of polygons approximating it.	     *
* Result is saved in the Polygons surface slot.				     *
* If however approximation alread exists, no computation is performed.       *
*****************************************************************************/
void ComputeSurfacePolygons(IPObjectStruct *PObj)
{
    int	i, j,
	Resolution = GetResolution(TRUE),
	FourPerFlat = GetFourPerFlat();
    char
	*SrfStrResolution = AttrGetObjectStrAttrib(PObj, "resolution");
    VectorType Vin;
    IPVertexStruct *V;
    IPPolygonStruct *P,
	*PHead = NULL;
    IPObjectStruct *PObjPoly;
    CagdPolygonStruct *CagdPoly, *CagdPolys;

    if (AttrGetObjectObjAttrib(PObj, "_polygons") != NULL)
	return;

    if (SrfStrResolution) {
	float SrfRelResolution;

	if (sscanf(SrfStrResolution, "%f", &SrfRelResolution) == 1) {
	    Resolution = REAL_TO_INT(Resolution * SrfRelResolution);
	}
    }

#ifndef __MSDOS__
    /* Make the resolution more reasonable (very slow on MSDOS). */
    Resolution *= 2;
#endif /* __MSDOS__ */

    if (AttrGetObjectStrAttrib(PObj, "twoperflat") != NULL)
	FourPerFlat = FALSE;
    if (AttrGetObjectStrAttrib(PObj, "fourperflat") != NULL)
	FourPerFlat = TRUE;

    CagdPolys = CagdSrf2Polygons(PObj -> U.Srfs, Resolution, TRUE,
							FourPerFlat, FALSE);

    for (CagdPoly = CagdPolys;
	 CagdPoly != NULL;
	 CagdPoly = CagdPoly -> Pnext) {
	/* All polygons are triangles! */
	P = IPAllocPolygon(0, 0, V = IPAllocVertex(0, 0, NULL, NULL), NULL);
	V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
	V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
	V -> Pnext = P -> PVertex;	   /* Make the Vertex list circular. */

	IP_SET_CONVEX_POLY(P);		       /* Mark it as convex polygon. */

	for (i = 0; i < 3; i++) {		     /* Convert to vertices. */
	    V = V -> Pnext;

	    for (j = 0; j < 3; j++)	     	   /* Convert to our format. */
		V -> Coord[j] = CagdPoly -> Polygon[i].Pt[j];
	    for (j = 0; j < 3; j++)
		V -> Normal[j] = CagdPoly -> Normal[i].Vec[j];
	}

	PT_ADD(Vin, CagdPoly -> Polygon[0].Pt, CagdPoly -> Normal[0].Vec);
	IritPrsrUpdatePolyPlane2(P, Vin);	   /* Update plane equation. */

	P -> Pnext = PHead;
	PHead = P;
    }

    CagdPolygonFreeList(CagdPolys);

    PObjPoly = GenPolyObject("", PHead, NULL);
    PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    AttrSetObjectObjAttrib(PObj, "_polygons", PObjPoly);
}

/*****************************************************************************
* Routine to convert a surface/list of surfaces into set of polygons.	     *
*****************************************************************************/
IPObjectStruct *Geometry2Polygons(IPObjectStruct *Obj)
{
    IPObjectStruct *PObj;

    if (IP_IS_SRF_OBJ(Obj)) {
	ComputeSurfacePolygons(Obj);
	PObj = CopyObject(NULL, AttrGetObjectObjAttrib(Obj, "_polygons"),
									FALSE);
	AttrSetObjectColor(PObj, GlblPrimColor);   /* Set its default color. */
	return PObj;
    }
    else if (IP_IS_OLST_OBJ(Obj))
    {
	int i = 0;
	IPPolygonStruct *P;
	IPObjectStruct
	    *PObjAll = NULL;

        while ((PObj = ListObjectGet(Obj, i++)) != NULL) {
	    PObj = Geometry2Polygons(PObj);

	    if (PObjAll) {
		if (PObj -> U.Pl) {
		    for (P = PObj -> U.Pl;
			 P -> Pnext != NULL;
			 P = P -> Pnext);
		    P -> Pnext = PObjAll -> U.Pl;
		    PObjAll -> U.Pl = PObj -> U.Pl;
		    PObj -> U.Pl = NULL;
		}
		IPFreeObject(PObj);
	    }
	    else {
		PObjAll = PObj;
	    }
	}

	return PObjAll;
    }
    else {
	WndwInputWindowPutStr("Unconvertable to polygons object ignored");
	return NULL;
    }
}

/*****************************************************************************
* Routine to convert a surface/curve/list of them into set of polylines.     *
*****************************************************************************/
IPObjectStruct *Geometry2Polylines(IPObjectStruct *Obj)
{
    IPObjectStruct *PObj, *PObjPolys;
    IPPolygonStruct *P, *PTail, *PHead;
    int DoMeshes = GetDrawCtlPt();

    if (IP_IS_SRF_OBJ(Obj)) {
	ComputeSurfaceIsoLines(Obj);
	if ((PObjPolys = AttrGetObjectObjAttrib(Obj, "_isoline")) != NULL)
    	    PHead = CopyPolygonList(PObjPolys -> U.Pl);
	else
	    PHead = NULL;

	if (DoMeshes &&
	    (PObjPolys = AttrGetObjectObjAttrib(Obj, "_ctlmesh")) != NULL) {
	    P = CopyPolygonList(PObjPolys -> U.Pl);

	    for (PTail = P; PTail -> Pnext != NULL; PTail = PTail -> Pnext);
	    PTail -> Pnext = PHead;
	    PHead = P;
	}

	PObj = GenPOLYObject(PHead);
	AttrSetObjectColor(PObj, GlblPrimColor);   /* Set its default color. */
	IP_SET_POLYLINE_OBJ(PObj);
	return PObj;
    }
    if (IP_IS_CRV_OBJ(Obj)) {
	ComputeCurveIsoLines(Obj);
	if ((PObjPolys = AttrGetObjectObjAttrib(Obj, "_isoline")) != NULL)
    	    PHead = CopyPolygonList(PObjPolys -> U.Pl);
	else
	    PHead = NULL;

	if (DoMeshes &&
	    (PObjPolys = AttrGetObjectObjAttrib(Obj, "_ctlpoly")) != NULL) {
	    P = CopyPolygonList(PObjPolys -> U.Pl);

	    for (PTail = P; PTail -> Pnext != NULL; PTail = PTail -> Pnext);
	    PTail -> Pnext = PHead;
	    PHead = P;
	}

	PObj = GenPolyObject("", PHead, NULL);
	AttrSetObjectColor(PObj, GlblPrimColor);   /* Set its default color. */
	IP_SET_POLYLINE_OBJ(PObj);
	return PObj;
    }
    else if (IP_IS_OLST_OBJ(Obj))
    {
	int i = 0;
	IPObjectStruct
	    *PObjAll = NULL;

        while ((PObj = ListObjectGet(Obj, i++)) != NULL) {
	    PObj = Geometry2Polylines(PObj);

	    if (PObjAll) {
		if (PObj -> U.Pl) {
		    for (P = PObj -> U.Pl;
			 P -> Pnext != NULL;
			 P = P -> Pnext);
		    P -> Pnext = PObjAll -> U.Pl;
		    PObjAll -> U.Pl = PObj -> U.Pl;
		    PObj -> U.Pl = NULL;
		}
		IPFreeObject(PObj);
	    }
	    else {
		PObjAll = PObj;
	    }
	}

	return PObjAll;
    }
    else {
	WndwInputWindowPutStr("Unconvertable to polylines object ignored");
	return NULL;
    }
}

/*****************************************************************************
* Creates a circle on the XY plane.					     *
*****************************************************************************/
IPObjectStruct *GenCircleCurveObject(VectorType Position, RealType *Radius)
{
    int i;
    CagdPtStruct Pos;
    CagdCrvStruct *CircCrv;
    IPObjectStruct *CrvObj;

    for (i = 0; i < 3; i++)
	Pos.Pt[i] = Position[i];
    CircCrv = BspCrvCreateCircle(&Pos, *Radius);

    if (CircCrv == NULL)
	return NULL;

    CrvObj = GenCRVObject(CircCrv);

    return CrvObj;
}

/*****************************************************************************
* Creates an arbitrary arc specified by Two end points and Center. Arc must  *
* be less than 180 degree.						     *
*****************************************************************************/
IPObjectStruct *GenArcCurveObject(VectorType Start, VectorType Center,
							VectorType End)
{
    int i;
    CagdPtStruct StartPt, CenterPt, EndPt;
    CagdCrvStruct *ArcCrv;
    IPObjectStruct *CrvObj;

    for (i = 0; i < 3; i++) {
	StartPt.Pt[i] = Start[i];
	CenterPt.Pt[i] = Center[i];
	EndPt.Pt[i] = End[i];
    }
    ArcCrv = BzrCrvCreateArc(&StartPt, &CenterPt, &EndPt);

    if (ArcCrv == NULL)
	return NULL;

    CrvObj = GenCRVObject(ArcCrv);

    return CrvObj;
}

/*****************************************************************************
* Construct a ruled surface out of the two provided curves. The two curves   *
* must have same point type/curve type/order (and knot vector if Bspline).   *
*****************************************************************************/
IPObjectStruct *GenRuledSrfObject(IPObjectStruct *Crv1, IPObjectStruct *Crv2)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*RuledSrf = CagdRuledSrf(Crv1 -> U.Crvs, Crv2 -> U.Crvs, 2, 2);

    if (RuledSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(RuledSrf);

    return SrfObj;
}

/*****************************************************************************
* Construct a boolean sum surface out of a single boundary curve.	     *
*****************************************************************************/
IPObjectStruct *GenBoolOneSrfObject(IPObjectStruct *BndryCrv)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*BoolSumSrf = CagdOneBoolSumSrf(BndryCrv -> U.Crvs);

    if (BoolSumSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(BoolSumSrf);

    return SrfObj;
}

/*****************************************************************************
* Construct a boolean sum surface out of the four provided curves.	     *
*****************************************************************************/
IPObjectStruct *GenBoolSumSrfObject(IPObjectStruct *Crv1, IPObjectStruct *Crv2,
				    IPObjectStruct *Crv3, IPObjectStruct *Crv4)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*BoolSumSrf = CagdBoolSumSrf(Crv1 -> U.Crvs,
				     Crv2 -> U.Crvs,
				     Crv3 -> U.Crvs,
				     Crv4 -> U.Crvs);

    if (BoolSumSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(BoolSumSrf);

    return SrfObj;
}

/*****************************************************************************
* Construct a surface out of the provided curve list.			     *
*****************************************************************************/
IPObjectStruct *GenSrfFromCrvsObject(IPObjectStruct *CrvList,
				     RealType *OtherOrder)
{
    int i,
	NumCrvs = 0;
    IPObjectStruct *SrfObj, *CrvObj, *PrevCrvObj, *HeadCrvObj;
    CagdSrfStruct *Srf;


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

    while ((CrvObj = ListObjectGet(CrvList, NumCrvs)) != NULL) {
	if (!IP_IS_CRV_OBJ(CrvObj)) {
	    WndwInputWindowPutStr("SURFACE: List contains non curve object(s).");
	    return NULL;
	}
	if (CrvObj -> U.Crvs -> Pnext != NULL) {
	    WndwInputWindowPutStr("SURFACE: nested curve lists are disallowed.");
	    return NULL;
	}
	NumCrvs++;
    }

    /* Chain all curves into a single list and invoke the srf constructor: */
    HeadCrvObj = PrevCrvObj = ListObjectGet(CrvList, 0);
    for (i = 1; i < NumCrvs; i++) {
	CrvObj = ListObjectGet(CrvList, i);
	PrevCrvObj -> U.Crvs -> Pnext = CrvObj -> U.Crvs;
	PrevCrvObj = CrvObj;
    }
    PrevCrvObj -> U.Crvs -> Pnext = NULL;

    Srf = CagdSrfFromCrvs(HeadCrvObj -> U.Crvs, REAL_PTR_TO_INT(OtherOrder));

    for (i = 0; i < NumCrvs; i++)
	ListObjectGet(CrvList, i) -> U.Crvs -> Pnext = NULL;

    if (Srf == NULL)
	return NULL;

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* Construct a Sweep surface out of the CrossSection curve, Axis curve and    *
* optional Scaling curve and Scaler which scales the CrossSection.	     *
*****************************************************************************/
IPObjectStruct *GenSweepSrfObject(IPObjectStruct *CrossSection,
				  IPObjectStruct *Axis, IPObjectStruct *Scale,
				  IPObjectStruct *Frame)
{
    IPObjectStruct *SrfObj;
    int FrameIsCrv = IP_IS_CRV_OBJ(Frame),
	HasFrame = FrameIsCrv || IP_IS_VEC_OBJ(Frame);
    CagdSrfStruct
	*SweepSrf = CagdSweepSrf(CrossSection -> U.Crvs, Axis -> U.Crvs,
				 IP_IS_CRV_OBJ(Scale) ? Scale -> U.Crvs : NULL,
				 IP_IS_NUM_OBJ(Scale) ? Scale -> U.R : 1.0,
				 HasFrame ? (FrameIsCrv
					        ? (VoidPtr) Frame -> U.Crvs
					        : (VoidPtr) Frame -> U.Vec)
					  : NULL,
				 FrameIsCrv);

    if (SweepSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(SweepSrf);

    return SrfObj;
}

/*****************************************************************************
* Computes an approximation to the offset of a (planar) curve or a surface.  *
*****************************************************************************/
IPObjectStruct *GenOffsetObject(IPObjectStruct *Obj, RealType *Offset)
{
    if (IP_IS_SRF_OBJ(Obj)) {
    	IPObjectStruct *SrfObj;
    	CagdSrfStruct
	    *OffsetSrf = CagdSrfOffset(Obj -> U.Srfs, *Offset);

    	if (OffsetSrf == NULL)
	    return NULL;

    	SrfObj = GenSRFObject(OffsetSrf);

    	return SrfObj;
    }
    else if (IP_IS_CRV_OBJ(Obj)) {
    	IPObjectStruct *CrvObj;
    	CagdCrvStruct
	    *OffsetCrv = CagdCrvOffset(Obj -> U.Crvs, *Offset);

    	if (OffsetCrv == NULL)
	    return NULL;

    	CrvObj = GenCRVObject(OffsetCrv);

    	return CrvObj;
    }
    else {
	WndwInputWindowPutStr("Offset allowed on curves/surfaces only");
	return NULL;
    }
}

/*****************************************************************************
* Computes an approximation to the offset of a (planar) curve or a surface.  *
* This offset is computed and approximated to with a given tolerance Epsilon *
* that is related to the square of the distance between the original curve   *
* and its offset approximation. If Trim then regions in the curve with       *
* curvature that is larger than offset distance required will be trimmed out.*
*****************************************************************************/
IPObjectStruct *GenAOffsetObject(IPObjectStruct *Obj, RealType *Offset,
					     RealType *Epsilon, RealType *Trim)
{
    if (IP_IS_SRF_OBJ(Obj)) {
	WndwInputWindowPutStr("Adaptive offset is not supported for surfaces");
	return NULL;
    }
    else if (IP_IS_CRV_OBJ(Obj)) {
    	IPObjectStruct *CrvObj;
    	CagdCrvStruct
	    *OffsetCrv = APX_EQ(*Trim, 0.0) ?
	        CagdCrvAdapOffset(Obj -> U.Crvs, *Offset, *Epsilon, NULL) :
		CagdCrvAdapOffsetTrim(Obj -> U.Crvs, *Offset,
				      *Epsilon, NULL);

    	if (OffsetCrv == NULL)
	    return NULL;
	else
	    CrvObj = GenCRVObject(OffsetCrv);

    	return CrvObj;
    }
    else {
	WndwInputWindowPutStr("Offset allowed on curves/surfaces only");
	return NULL;
    }
}

/*****************************************************************************
* Computes curvature properties of the given curve.			     *
*****************************************************************************/
IPObjectStruct *CrvCurvaturePts(IPObjectStruct *PObj, RealType *Eps)
{ 
    IPObjectStruct *NewPObj;

    if (CAGD_NUM_OF_PT_COORD(PObj -> U.Crvs -> PType) < 2 ||
	CAGD_NUM_OF_PT_COORD(PObj -> U.Crvs -> PType) > 3) {
	WndwInputWindowPutStr(
	    "CCRVTR: Only 2 or 3 dimensional curves are supported.");
	return NULL;
    }

    if (*Eps <= 0.0) {
	if (CAGD_NUM_OF_PT_COORD(PObj -> U.Crvs -> PType) == 3) {
	    CagdCrvStruct
		*CrvtrCrv = CagdCrvCurvatureNormal(PObj -> U.Crvs);

	    NewPObj = GenCRVObject(CrvtrCrv);
	}
	else {
	    CagdRType TMin, TMax;
	    CagdCrvStruct *CrvtrCrv2D,
		*CrvtrCrv = CagdCrvCurvatureSqr(PObj -> U.Crvs);

	    CagdCrvDomain(PObj -> U.Crvs, &TMin, &TMax);
	    CrvtrCrv2D = CagdPrmtSclrCrvTo2D(CrvtrCrv, TMin, TMax);
	    CagdCrvFree(CrvtrCrv);

	    NewPObj = GenCRVObject(CrvtrCrv2D);
	}
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = CagdCrvExtremCrvtrPts(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;
}

/*****************************************************************************
* Merge two surfaces into one in specified Dir and SameEdge flag. 	     *
*****************************************************************************/
IPObjectStruct *MergeSrfSrf(IPObjectStruct *Srf1, IPObjectStruct *Srf2,
					     RealType *Dir, RealType *SameEdge)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*Srf = CagdMergeSrfSrf(Srf1 -> U.Srfs, Srf2 -> U.Srfs,
			       REAL_PTR_TO_INT(Dir), REAL_PTR_TO_INT(SameEdge));

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* Merge two curves/ctl points into one curve by adding a linear segment      *
* between the first end point to second start point.			     *
*****************************************************************************/
IPObjectStruct *MergeCurvesAndCtlPoints(IPObjectStruct *PObj1,
					IPObjectStruct *PObj2)
{
    IPObjectStruct *CrvObj;
    CagdCrvStruct
	*Crv = NULL;
    CagdPtStruct Pt1, Pt2;

    if (IP_IS_CRV_OBJ(PObj1)) {
	if (IP_IS_CRV_OBJ(PObj2)) {
	    Crv = CagdMergeCrvCrv(PObj1 -> U.Crvs, PObj2 -> U.Crvs);
	}
	else if (IP_IS_CTLPT_OBJ(PObj2)) {
	    CagdRType
		*Coords2 = PObj2 -> U.CtlPt.Coords;

	    CagdCoerceToE3(Pt2.Pt, &Coords2, -1, PObj2 -> U.CtlPt.PtType);
	    Crv = CagdMergeCrvPt(PObj1 -> U.Crvs, &Pt2);
	}
	else
	    IritFatalError("Curve/CtlPt was expected.");
    }
    else if (IP_IS_CTLPT_OBJ(PObj1)) {
	CagdRType
	    *Coords1 = PObj1 -> U.CtlPt.Coords;

	CagdCoerceToE3(Pt1.Pt, &Coords1, -1, PObj1 -> U.CtlPt.PtType);

	if (IP_IS_CRV_OBJ(PObj2)) {
	    Crv = CagdMergePtCrv(&Pt1, PObj2 -> U.Crvs);
	}
	else if (IP_IS_CTLPT_OBJ(PObj2)) {
	    CagdRType
		*Coords2 = PObj2 -> U.CtlPt.Coords;

	    CagdCoerceToE3(Pt2.Pt, &Coords2, -1, PObj2 -> U.CtlPt.PtType);
	    Crv = CagdMergePtPt(&Pt1, &Pt2);
	}
	else
	    IritFatalError("Curve/CtlPt was expected.");
    }
    else
	IritFatalError("Curve/CtlPt was expected.");

    if (Crv == NULL)
	return NULL;

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
*  Editing of a single control point of a curve.			     *
*****************************************************************************/
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;
}

/*****************************************************************************
*  Editing of a single control point of a surface.			     *
*****************************************************************************/
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;
}

/*****************************************************************************
*  Editing of a single control point of a curve.			     *
*****************************************************************************/
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) {
	WndwInputWindowPutStr("Order to raise less the current");
	return NULL;
    }

    Crv = CagdCrvDegreeRaiseN(Crv, NewOrder);

    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
*  Editing of a single control point of a curve.			     *
*****************************************************************************/
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;
}

/*****************************************************************************
*  Make, in place, the following two surfaces compatible.		     *
*****************************************************************************/
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.");
    }
}

/*****************************************************************************
*  Compute a new surface, which is the convex blend of the given two	     *
* compatible surfaces. Blend must be between 0 and 1 for a convex blend.     *
*****************************************************************************/
IPObjectStruct *TwoSrfsMorphing(IPObjectStruct *PObjSrf1,
				IPObjectStruct *PObjSrf2, RealType *Blend)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*Srf = CagdTwoSrfsMorphing(PObjSrf1 -> U.Srfs, PObjSrf2 -> U.Srfs,
									*Blend);

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

    return SrfObj;
}

/*****************************************************************************
*  Convert a Bezier curve or surface into a Bspline curve or surface.        *
*****************************************************************************/
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("Onlu surface/curve expected.");
        return NULL;
    }
}

/*****************************************************************************
* Convert a Bspline curve or surface into list of Bezier curves or surfaces. *
*****************************************************************************/
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("Onlu surface/curve expected.");
        return NULL;
    }
}

/*****************************************************************************
* Compute a new crv/srf, which is the product of the two given crvs/srfs.    *
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsProduct(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = CagdSrfMult(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 = CagdCrvMult(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

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

    return Obj;
}

/*****************************************************************************
* Compute a new crv/srf, which is the dot product of the two given crvs/srfs.*
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsDotProduct(IPObjectStruct *PObj1,
				      IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = CagdSrfDotProd(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 = CagdCrvDotProd(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

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

    return Obj;
}

/*****************************************************************************
* Compute a crv/srf, which is the cross product of the two given crvs/srfs.  *
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsCrossProduct(IPObjectStruct *PObj1,
					IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = CagdSrfCrossProd(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 = CagdCrvCrossProd(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

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

    return Obj;
}

/*****************************************************************************
*  Compute a new crv/srf, which is the sum of the two given crvs/srfs.	     *
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsSum(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = CagdSrfAdd(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 = CagdCrvAdd(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

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

    return Obj;
}

/*****************************************************************************
*  Compute a new crv/srf, which is the difference of the two given crvs/srfs.*
*****************************************************************************/
IPObjectStruct *TwoCrvsSrfsDiff(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

    if (IP_IS_SRF_OBJ(PObj1) && IP_IS_SRF_OBJ(PObj2)) {
	CagdSrfStruct
	    *Srf = CagdSrfSub(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 = CagdCrvSub(PObj1 -> U.Crvs, PObj2 -> U.Crvs);

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

    return Obj;
}

/*****************************************************************************
*  Compute the inflection points of a curve.				     *
*****************************************************************************/
IPObjectStruct *CrvInflectionPts(IPObjectStruct *PObj, RealType *Eps)
{
    IPObjectStruct *NewPObj;

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

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

	NewPObj = GenCRVObject(InflectCrv2D);
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = CagdCrvInflectionPts(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;
}

/*****************************************************************************
*  Compute the zero points of a curve.					     *
*****************************************************************************/
IPObjectStruct *CrvZeros(IPObjectStruct *PObj, RealType *Eps, RealType *Axis)
{
    int i;
    CagdPtStruct *ZPtsTmp,
	*ZPts = CagdCrvZeroSet(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;
}

/*****************************************************************************
*  Compute the extreme points of a curve.				     *
*****************************************************************************/
IPObjectStruct *CrvExtremes(IPObjectStruct *PObj, RealType *Eps,
								RealType *Axis)
{
    int i;
    CagdPtStruct *ExPtsTmp,
	*ExPts = CagdCrvExtremSet(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;
}

/*****************************************************************************
*  Compute the intersection points of two curves.			     *
*****************************************************************************/
IPObjectStruct *CrvCrvInter(IPObjectStruct *PObj1, IPObjectStruct *PObj2,
			    RealType *Eps, RealType *SelfInter)
{
    CagdSrfStruct
	*DistSrf = CagdSrfDistCrvCrv(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 = CagdPrmtSclrSrfTo3D(DistSrf, UMin, UMax, VMin, VMax);
	CagdSrfFree(DistSrf);

	NewPObj = GenSRFObject(DistSrf3D);
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = CagdSrfDistFindPoints(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;
}

/*****************************************************************************
*  Compute the closest point(s) on a curve to a given point.		     *
*****************************************************************************/
IPObjectStruct *CrvPointDist(IPObjectStruct *PCrv, PointType Point,
					     RealType *MinDist, RealType *Eps)
{
    IPObjectStruct *NewPObj;

    if (*Eps > 0.0) {
	NewPObj = GenNUMValObject(CagdDistCrvPoint(PCrv -> U.Crvs,
						   Point,
						   REAL_PTR_TO_INT(MinDist),
						   *Eps));
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = CagdLclDistCrvPoint(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;
}

/*****************************************************************************
*  Compute the closest point(s) on a curve to a given line.		     *
*****************************************************************************/
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(CagdDistCrvLine(PCrv -> U.Crvs,
						  Line,
						  REAL_PTR_TO_INT(MinDist),
						  *Eps));
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = CagdLclDistCrvLine(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;
}

/*****************************************************************************
*  Compose a curve on a curve or a surface.				     *
*****************************************************************************/
IPObjectStruct *CrvComposition(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct
	*Obj = NULL;

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

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

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

    return Obj;
}

/*****************************************************************************
*  Computes a layout (prisa) of the given set of surfaces.		     *
*****************************************************************************/
IPObjectStruct *SrfsPrisa(IPObjectStruct *Srfs, CagdRType *SamplesPerCurve,
			CagdRType *Epsilon, CagdRType *Dir, CagdVType Space)
{
    int i;
    IPObjectStruct *PObjList, *PObj;
    CagdSrfStruct *Srf,
	*PrisaSrfs = CagdAllPrisaSrfs(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;
}

/*****************************************************************************
*  Computes a layout (prisa) of the given set of surfaces.		     *
*****************************************************************************/
IPObjectStruct *SrfAdapIsoCurves(IPObjectStruct *Srf, CagdRType *Dir,
		 CagdRType *Eps, CagdRType *FullIso, CagdRType *SinglePath)
{
    IPObjectStruct *NewPObj;
    CagdCrvStruct
	*Crv = CagdAdapIsoExtract(Srf -> U.Srfs, NULL, REAL_PTR_TO_INT(Dir),
				  *Eps, REAL_PTR_TO_INT(FullIso),
				  REAL_PTR_TO_INT(SinglePath));

    NewPObj = GenCRVObject(Crv);

    return NewPObj;
}

/*****************************************************************************
*  Computes a layout (prisa) of the given set of surfaces.		     *
*****************************************************************************/
IPObjectStruct *GetCrvSrfPDomain(IPObjectStruct *SrfOrCrv)
{
    int i, n;
    IPObjectStruct *NewPObj;
    CagdRType Domain[4];

    if (IP_IS_CRV_OBJ(SrfOrCrv)) {
	CagdCrvDomain(SrfOrCrv -> U.Crvs, &Domain[0], &Domain[1]);
	n = 2;
    }
    else if (IP_IS_SRF_OBJ(SrfOrCrv)) {
	CagdSrfDomain(SrfOrCrv -> U.Srfs,
		      &Domain[0], &Domain[1], &Domain[2], &Domain[3]);
	n = 4;
    }
    else {
	IritFatalError("Curve or surface 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;
}
