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

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

static IPObjectStruct *ComputeCurveIsoLines(IPObjectStruct *PObj,
					    int Optimal);
static IPObjectStruct *ComputeSurfaceIsoLines(IPObjectStruct *PObj,
					      int Optimal);
static IPObjectStruct *ComputeTrimSrfIsoLines(IPObjectStruct *PObj,
					      int Optimal);
static IPObjectStruct *ComputeTrivarIsoLines(IPObjectStruct *PObj,
					     int Optimal);
static IPObjectStruct *ComputeTriSrfIsoLines(IPObjectStruct *PObj,
					     int Optimal);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to reverse a curve.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvObj:     Curve to reverse its parametrization.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Reversed curve.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CurveReverse                                                             M
*****************************************************************************/
IPObjectStruct *CurveReverse(IPObjectStruct *CrvObj)
{
     CagdCrvStruct
	*RevCrv = CagdCrvReverse(CrvObj -> U.Crvs);

    if (RevCrv == NULL)
	return NULL;

    CrvObj = GenCRVObject(RevCrv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to reverse a surface.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfObj:     Surface to reverse.                                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Reversed surface. The normal of the reversed        M
*                        surface is flipped with respected to the original   M
*                        surface, SrfObj, by 180 degrees.		     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SurfaceReverse                                                           M
*****************************************************************************/
IPObjectStruct *SurfaceReverse(IPObjectStruct *SrfObj)
{
    CagdSrfStruct
	*RevSrf = CagdSrfReverse(SrfObj -> U.Srfs);

    if (RevSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(RevSrf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to convert curve(s) to a piecewise linear polyline approximation   *
* and convert its control polygon into polyline as well.		     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:       Curves to approximate using piecewise linear approximation.  *
*   Optimal:    Do we want an optimal sampling but expensive approach?       *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:   A polyline approximation to PObj.                    *
*****************************************************************************/
static IPObjectStruct *ComputeCurveIsoLines(IPObjectStruct *PObj, int Optimal)
{
    int Resolution = GetResolution(FALSE);
    IPObjectStruct *PObjPoly;
    CagdCrvStruct *Crv;

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

    if (Resolution < MIN_FREE_FORM_RES)
	Resolution = MIN_FREE_FORM_RES;

    PObjPoly = GenPOLYObject(NULL);
    PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    IP_SET_POLYLINE_OBJ(PObjPoly);
    for (Crv = PObj -> U.Crvs; Crv != NULL; Crv = Crv -> Pnext) {
	IPPolygonStruct *Poly;

	Poly = IritCurve2Polylines(Crv, Resolution,
				   (SymbCrvApproxMethodType) Optimal);

	Poly -> Pnext = PObjPoly -> U.Pl;
	PObjPoly -> U.Pl = Poly;
    }

    return PObjPoly;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to convert a surface to a set of piecewise linear polyline	     *
* approximation and convert its control mesh into set of polyline as well.   *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:        Surfaces to apporximate as a mesh of piecewise linear       *
*		 isocurves.                                                  *
*   Optimal:     Do we want an optimal sampling but expensive approach?      *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:  A polyline object approximating PObj.                 *
*****************************************************************************/
static IPObjectStruct *ComputeSurfaceIsoLines(IPObjectStruct *PObj,
					      int Optimal)
{
    int Resolution = GetResolution(FALSE);
    IPObjectStruct *PObjPolys;
    CagdSrfStruct *Srf;

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

    if (Resolution < MIN_FREE_FORM_RES)
	Resolution = MIN_FREE_FORM_RES;

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

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

	for (PolyTmp = Polys;
	     PolyTmp -> Pnext != NULL;
	     PolyTmp = PolyTmp -> Pnext);
	PolyTmp -> Pnext = PObjPolys -> U.Pl;
	PObjPolys -> U.Pl = Polys;
    }

    return PObjPolys;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to convert a trimmed surface to a set of piecewise linear polyline *
* approximation and convert its control mesh into set of polyline as well.   *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:        Trimmed surfaces to apporximate as a mesh of piecewise      *
*		  linear isocurves.                                          *
*   Optimal:     Do we want an optimal sampling but expensive approach?      *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:  A polyline object approximating PObj.                 *
*****************************************************************************/
static IPObjectStruct *ComputeTrimSrfIsoLines(IPObjectStruct *PObj,
					      int Optimal)
{
    int Resolution = GetResolution(FALSE);
    IPObjectStruct *PObjPolys;
    TrimSrfStruct *TrimSrf;

    if (!IP_IS_TRIMSRF_OBJ(PObj))
	IritFatalError("Trimmed surface was expected.");

    if (Resolution < MIN_FREE_FORM_RES)
	Resolution = MIN_FREE_FORM_RES;

    PObjPolys = GenPOLYObject(NULL);
    PObjPolys -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    IP_SET_POLYLINE_OBJ(PObjPolys);
    for (TrimSrf = PObj -> U.TrimSrfs;
	 TrimSrf != NULL;
	 TrimSrf = TrimSrf -> Pnext) {
	int NumOfIso[2];
	IPPolygonStruct *Polys, *PolyTmp;

	NumOfIso[0] = NumOfIso[1] = -Resolution;
	Polys = IritTrimSrf2Polylines(TrimSrf, NumOfIso, Resolution,
				      (SymbCrvApproxMethodType) Optimal,
				      TRUE, TRUE);

	for (PolyTmp = Polys;
	     PolyTmp -> Pnext != NULL;
	     PolyTmp = PolyTmp -> Pnext);
	PolyTmp -> Pnext = PObjPolys -> U.Pl;
	PObjPolys -> U.Pl = Polys;
    }

    return PObjPolys;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to convert a trimmed surface to a set of piecewise linear polyline *
* approximation and convert its control mesh into set of polyline as well.   *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:        Trimmed surfaces to apporximate as a mesh of piecewise      *
*		  linear isocurves.                                          *
*   Optimal:     Do we want an optimal sampling but expensive approach?      *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:  A polyline object approximating PObj.                 *
*****************************************************************************/
static IPObjectStruct *ComputeTrivarIsoLines(IPObjectStruct *PObj,
					     int Optimal)
{
    int Resolution = GetResolution(FALSE);
    IPObjectStruct *PObjPolys;
    TrivTVStruct *Trivar;

    if (!IP_IS_TRIVAR_OBJ(PObj))
	IritFatalError("Trivariate function was expected.");

    if (Resolution < MIN_FREE_FORM_RES)
	Resolution = MIN_FREE_FORM_RES;

    PObjPolys = GenPOLYObject(NULL);
    PObjPolys -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    IP_SET_POLYLINE_OBJ(PObjPolys);
    for (Trivar = PObj -> U.Trivars;
	 Trivar != NULL;
	 Trivar = Trivar -> Pnext) {
	int NumOfIso[3],
	    Res = MAX(Resolution / 2, 2);
	IPPolygonStruct *Polys, *PolyTmp;

	NumOfIso[0] = NumOfIso[1] = NumOfIso[2] = -Res;
	Polys = IritTrivar2Polylines(Trivar, NumOfIso, Resolution,
				     (SymbCrvApproxMethodType) Optimal);

	for (PolyTmp = Polys;
	     PolyTmp -> Pnext != NULL;
	     PolyTmp = PolyTmp -> Pnext);
	PolyTmp -> Pnext = PObjPolys -> U.Pl;
	PObjPolys -> U.Pl = Polys;
    }

    return PObjPolys;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to convert a triangular surface to a set of piecewise linear	     *
* polyline approximation and convert its control mesh into set of polyline   *
* as well.								     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:        Triangular surfaces to apporximate as a mesh of piecewise   *
*		 linear isocurves.                                           *
*   Optimal:     Do we want an optimal sampling but expensive approach?      *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:  A polyline object approximating PObj.                 *
*****************************************************************************/
static IPObjectStruct *ComputeTriSrfIsoLines(IPObjectStruct *PObj,
					     int Optimal)
{
    int Resolution = GetResolution(FALSE);
    IPObjectStruct *PObjPolys;
    TrngTriangSrfStruct *TriSrf;

    if (!IP_IS_TRISRF_OBJ(PObj))
	IritFatalError("Triangular surface was expected.");

    if (Resolution < MIN_FREE_FORM_RES)
	Resolution = MIN_FREE_FORM_RES;

    PObjPolys = GenPOLYObject(NULL);
    PObjPolys -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    IP_SET_POLYLINE_OBJ(PObjPolys);
    for (TriSrf = PObj -> U.TriSrfs;
	 TriSrf != NULL;
	 TriSrf = TriSrf -> Pnext) {
	int NumOfIso[3];
	IPPolygonStruct *Polys, *PolyTmp;

	NumOfIso[0] = NumOfIso[1] = NumOfIso[2] = -Resolution;
	Polys = IritTriSrf2Polylines(TriSrf, NumOfIso, Resolution, Optimal);

	for (PolyTmp = Polys;
	     PolyTmp -> Pnext != NULL;
	     PolyTmp = PolyTmp -> Pnext);
	PolyTmp -> Pnext = PObjPolys -> U.Pl;
	PObjPolys -> U.Pl = Polys;
    }

    return PObjPolys;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert a surface to a set of polygons approximating it.	     M
*   Result is saved as an attribute on the surface.			     M
*   If, however, approximation already exists, no computation is performed.  M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Surface to approximate using polygons.                        M
*   Normals:   Compute normals as well, if TRUE.	                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   ComputeSurfacePolygons                                                   M
*****************************************************************************/
void ComputeSurfacePolygons(IPObjectStruct *PObj, int Normals)
{
    int	Resolution = GetResolution(FALSE),
	FourPerFlat = GetFourPerFlat(),
	PolyApproxOpt = GetPolyApproxOptimal(),
	PolyApproxUV = GetPolyApproxUV(),
        BoolUVBooleanState = BoolSetParamSurfaceUVVals(TRUE);
    RealType t,
	RelResolution = AttrGetObjectRealAttrib(PObj, "resolution"),
	Tolerance =  GetPolyApproxTol();
    IPPolygonStruct *Polys;
    IPObjectStruct *PObjPoly;

    BoolSetParamSurfaceUVVals(BoolUVBooleanState);/* Restore bool_lib state. */

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

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

#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;

    if ((t = AttrGetObjectRealAttrib(PObj, "u_resolution")) < IP_ATTR_BAD_REAL)
        AttrSetRealAttrib(&PObj -> U.Srfs -> Attr, "u_resolution", t);
    if ((t = AttrGetObjectRealAttrib(PObj, "v_resolution")) < IP_ATTR_BAD_REAL)
        AttrSetRealAttrib(&PObj -> U.Srfs -> Attr, "v_resolution", t);

    Polys = IritSurface2Polygons(PObj -> U.Srfs, FourPerFlat,
				 PolyApproxOpt ? Tolerance : Resolution,
				 PolyApproxUV || BoolUVBooleanState,
				 Normals, PolyApproxOpt);
    PObjPoly = GenPolyObject("", Polys, NULL);
    PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    AttrSetObjectObjAttrib(PObj, "_polygons", PObjPoly, FALSE);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert a triangular surface to a set of polygons		     M
* approximating it.							     M
*   Result is saved as an attribute on the surface.			     M
*   If, however, approximation already exists, no computation is performed.  M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Triangular surface to approximate using polygons.             M
*   Normals:   Compute normals as well, if TRUE.	                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   ComputeTriSrfPolygons                                                    M
*****************************************************************************/
void ComputeTriSrfPolygons(IPObjectStruct *PObj, int Normals)
{
    int	Resolution = GetResolution(FALSE),
	PolyApproxOpt = GetPolyApproxOptimal(),
	PolyApproxUV = GetPolyApproxUV(),
        BoolUVBooleanState = BoolSetParamSurfaceUVVals(TRUE);
    RealType
	RelResolution = AttrGetObjectRealAttrib(PObj, "resolution"),
	Tolerance =  GetPolyApproxTol();
    IPPolygonStruct *Polys;
    IPObjectStruct *PObjPoly;

    BoolSetParamSurfaceUVVals(BoolUVBooleanState);/* Restore bool_lib state. */

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

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

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

    Polys = IritTriSrf2Polygons(PObj -> U.TriSrfs,
				PolyApproxOpt ? Tolerance : Resolution,
				PolyApproxUV || BoolUVBooleanState,
				Normals, PolyApproxOpt);
    PObjPoly = GenPolyObject("", Polys, NULL);
    PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    AttrSetObjectObjAttrib(PObj, "_polygons", PObjPoly, FALSE);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert a trimmed surface to a set of polygons approximating it.M
*   Result is saved as an attribute on the trimmed surface.		     M
*   If, however, approximation already exists, no computation is performed.  M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Trimmed surface to approximate using polygons.                M
*   Normals:   Compute normals as well, if TRUE.	                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   ComputeTrimSrfPolygons                                                   M
*****************************************************************************/
void ComputeTrimSrfPolygons(IPObjectStruct *PObj, int Normals)
{
    int	Resolution = GetResolution(FALSE),
	FourPerFlat = GetFourPerFlat(),
	PolyApproxOpt = GetPolyApproxOptimal(),
	PolyApproxUV = GetPolyApproxUV();
    RealType t,
	RelResolution = AttrGetObjectRealAttrib(PObj, "resolution"),
	Tolerance =  GetPolyApproxTol();
    IPPolygonStruct *Polys;
    IPObjectStruct *PObjPoly;

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

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

#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;

    if ((t = AttrGetObjectRealAttrib(PObj, "u_resolution")) < IP_ATTR_BAD_REAL)
        AttrSetRealAttrib(&PObj -> U.TrimSrfs -> Attr, "u_resolution", t);
    if ((t = AttrGetObjectRealAttrib(PObj, "v_resolution")) < IP_ATTR_BAD_REAL)
        AttrSetRealAttrib(&PObj -> U.TrimSrfs -> Attr, "v_resolution", t);

    Polys = IritTrimSrf2Polygons(PObj -> U.TrimSrfs, FourPerFlat,
				 PolyApproxOpt ? Tolerance : Resolution,
				 PolyApproxUV, Normals, PolyApproxOpt);
    PObjPoly = GenPolyObject("", Polys, NULL);
    PObjPoly -> Attrs = AttrCopyAttributes(PObj -> Attrs);
    AttrSetObjectObjAttrib(PObj, "_polygons", PObjPoly, FALSE);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert a surface/list of surfaces into set of polygons.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:       A geometry to convert and approximate using polygons.	     M
*   RNormals:   Do we want normals as well (at vertices)?                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A polygonal object approximating Obj.                M
*                                                                            *
* KEYWORDS:                                                                  M
*   Geometry2Polygons                                                        M
*****************************************************************************/
IPObjectStruct *Geometry2Polygons(IPObjectStruct *Obj, RealType *RNormals)
{
    IPObjectStruct *PObj;
    int Normals = REAL_PTR_TO_INT(RNormals);

    if (IP_IS_SRF_OBJ(Obj) ||
	IP_IS_TRISRF_OBJ(Obj) ||
	IP_IS_TRIMSRF_OBJ(Obj)) {
	if (AttrGetObjectObjAttrib(Obj, "_polygons") != NULL)
	    AttrFreeOneAttribute(&Obj -> Attrs, "_polygons");

	if (IP_IS_SRF_OBJ(Obj))
	    ComputeSurfacePolygons(Obj, Normals);
	else if (IP_IS_TRISRF_OBJ(Obj))
	    ComputeTriSrfPolygons(Obj, Normals);
	else if (IP_IS_TRIMSRF_OBJ(Obj))
	    ComputeTrimSrfPolygons(Obj, Normals);

	PObj = CopyObject(NULL, AttrGetObjectObjAttrib(Obj, "_polygons"),
									FALSE);
	AttrSetObjectColor(PObj, GlblPrimColor);   /* Set its default color. */

	if (!Normals) {
	    IPPolygonStruct *Pl;

	    for (Pl = PObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) {
		IPVertexStruct
		    *V = Pl -> PVertex;

		do {
		    IP_RST_NORMAL_VRTX(V);

		    V = V -> Pnext;
		}
		while (V != NULL && V != Pl -> PVertex);
	    }
	}

	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, RNormals);

	    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;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert a surface/curve/list of these into set of polylines.    M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:       A geometry to convert and approximate using polygons.	     M
*   Optimal:   Do we want an optimal sampling but expensive approach?        M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A polyline object approximating Obj.                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   Geometry2Polylines                                                       M
*****************************************************************************/
IPObjectStruct *Geometry2Polylines(IPObjectStruct *Obj, RealType *Optimal)
{
    IPObjectStruct *PObj;
    IPPolygonStruct *P;

    if (IP_IS_CRV_OBJ(Obj)) {
	return ComputeCurveIsoLines(Obj, REAL_PTR_TO_INT(Optimal));
    }
    else if (IP_IS_SRF_OBJ(Obj)) {
	return ComputeSurfaceIsoLines(Obj, REAL_PTR_TO_INT(Optimal));
    }
    else if (IP_IS_TRIMSRF_OBJ(Obj)) {
	return ComputeTrimSrfIsoLines(Obj, REAL_PTR_TO_INT(Optimal));
    }
    else if (IP_IS_TRIVAR_OBJ(Obj)) {
	return ComputeTrivarIsoLines(Obj, REAL_PTR_TO_INT(Optimal));
    }
    else if (IP_IS_TRISRF_OBJ(Obj)) {
	return ComputeTriSrfIsoLines(Obj, REAL_PTR_TO_INT(Optimal));
    }
    else if (IP_IS_OLST_OBJ(Obj))
    {
	int i = 0;
	IPObjectStruct
	    *PObjAll = NULL;

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

	    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 {
	return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to return extremum value of control mesh/polygon.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:      Object to bound itsextremum possible values.                   M
*   Min:      TRUE for minimum, FALSE for maximum.                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A control points with extremum values. Rational are  M
*                       Correctly porjected back to Euclidean space.         M
*                                                                            *
* KEYWORDS:                                                                  M
*   ExtremumControlPointVals                                                 M
*****************************************************************************/
IPObjectStruct *ExtremumControlPointVals(IPObjectStruct *Obj, CagdRType *Min)
{
    CagdBType
	FindMin = REAL_PTR_TO_INT(Min);
    CagdRType *Extremum,
	**Points = NULL;
    int Len = 0;
    CagdPointType
	PType  = CAGD_PT_E1_TYPE;

    if (IP_IS_SRF_OBJ(Obj)) {
	Points = Obj -> U.Srfs -> Points;
	Len = Obj -> U.Srfs -> ULength * Obj -> U.Srfs -> VLength;
	PType = Obj -> U.Srfs -> PType;
    }
    else if (IP_IS_CRV_OBJ(Obj)) {
	Points = Obj -> U.Crvs -> Points;
	Len = Obj -> U.Crvs -> Length;
	PType = Obj -> U.Crvs -> PType;
    }
    else {
	IritNonFatalError("Extremum allowed on curves/surfaces only");
	return NULL;
    }

    Extremum = SymbExtremumCntPtVals(Points, Len, FindMin);

    return GenCTLPTObject(PType, Extremum, NULL);
}
 
/*****************************************************************************
* DESCRIPTION:                                                               M
* Creates an exact rational quadratic circle parallel to the XY plane.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Position:   Center of circle.                                            M
*   Radius:     Radius of circle.                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A rational quadratic Bspline curve object 	     M
*			representing a circle.				     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenCircleCurveObject                                                     M
*****************************************************************************/
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;
}
 
/*****************************************************************************
* DESCRIPTION:                                                               M
* Creates an approximated cubic polynomial circle parallel to the XY plane.  M
*                                                                            *
* PARAMETERS:                                                                M
*   Position:   Center of circle.                                            M
*   Radius:     Radius of circle.                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A cubic polynomial Bspline curve object 	     M
*			representing a circle.				     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenPCircleCurveObject                                                    M
*****************************************************************************/
IPObjectStruct *GenPCircleCurveObject(VectorType Position, RealType *Radius)
{
    int i;
    CagdPtStruct Pos;
    CagdCrvStruct *CircCrv;
    IPObjectStruct *CrvObj;

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

    if (CircCrv == NULL)
	return NULL;

    CrvObj = GenCRVObject(CircCrv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Creates an arbitrary arc specified by Two end points and Center. Arc must  M
* be less than 180 degree.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   Start:    Location of arc.                                               M
*   Center:   Location of arc.                                               M
*   End:      Location of arc.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A curve representing the requested arc.               M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenArcCurveObject                                                        M
*****************************************************************************/
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;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Constructs a ruled surface out of the two provided curves.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj1, Obj2:  Two polys/curves to rule a surface between.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A ruled surface object.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenRuledSrfObject                                                        M
*****************************************************************************/
IPObjectStruct *GenRuledSrfObject(IPObjectStruct *Obj1, IPObjectStruct *Obj2)
{
    IPObjectStruct
	*SrfObj = NULL;

    if (IP_IS_POLY_OBJ(Obj1) && IP_IS_POLY_OBJ(Obj2)) {
	SrfObj = PrimGenRULEDObject(Obj1, Obj2);
    }
    else if (IP_IS_CRV_OBJ(Obj1) && IP_IS_CRV_OBJ(Obj2)) {
	CagdSrfStruct
	    *RuledSrf = CagdRuledSrf(Obj1 -> U.Crvs, Obj2 -> U.Crvs, 2, 2);

	if (RuledSrf == NULL)
	    return NULL;

	SrfObj = GenSRFObject(RuledSrf);
    }
    else {
	IritFatalError("RuledSrf: improper input geometry to ruled surface!");
    }

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Construct a boolean sum surface out of a single boundary curve.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   BndryCrv:  A curve to fill its interior with a surface. Better be close. M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: A filling surface object.                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBoolOneSrfObject                                                      M
*****************************************************************************/
IPObjectStruct *GenBoolOneSrfObject(IPObjectStruct *BndryCrv)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*BoolSumSrf = CagdOneBoolSumSrf(BndryCrv -> U.Crvs);

    if (BoolSumSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(BoolSumSrf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Construct a boolean sum surface out of the four provided curves.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Crv1, Crv2, Crv3, Crv4:  Four curves to construct a Boolean sum between. M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  The Boolean sum surface.                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenBoolSumSrfObject                                                      M
*****************************************************************************/
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;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Construct a surface out of the provided curve list.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvList:     A list of curves to approximate a surface through.          M
*   OtherOrder:  Other order of surface.                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A surface approximately traversing through the given M
*                       curves.                                              M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenSrfFromCrvsObject                                                     M
*****************************************************************************/
IPObjectStruct *GenSrfFromCrvsObject(IPObjectStruct *CrvList,
				     RealType *OtherOrder)
{
    int i,
	NumCrvs = 0;
    IPObjectStruct *SrfObj, *CrvObj;
    CagdSrfStruct *Srf;
    CagdCrvStruct *Crv,
	*Crvs = NULL;

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

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

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

    Crvs = CagdListReverse(Crvs);
    Srf = CagdSrfFromCrvs(Crvs, REAL_PTR_TO_INT(OtherOrder));
    CagdCrvFreeList(Crvs);

    if (Srf == NULL)
	return NULL;

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Constructs a Sweep surface out of the CrossSection curve, Axis curve and   M
* optional Scaling curve and Scaler which scales the CrossSection.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrossSection:  Cross section of sweep.                                   M
*   Axis:          Axis curve of sweep.                                      M
*   Frame:         Orientation specification. Either an orientation curve    M
*		   Or a vector specification.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A sweep surface.                                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenSweepSrfObject                                                        M
*****************************************************************************/
IPObjectStruct *GenSweepSrfObject(IPObjectStruct *CrossSection,
				  IPObjectStruct *Axis,
				  IPObjectStruct *Frame)
{
    IPObjectStruct *SrfObj;
    int FrameIsCrv = IP_IS_CRV_OBJ(Frame),
	HasFrame = FrameIsCrv || IP_IS_VEC_OBJ(Frame);
    CagdSrfStruct *SweepSrf;

    SweepSrf = CagdSweepSrf(CrossSection -> U.Crvs, Axis -> U.Crvs, NULL, 1.0,
			    HasFrame ? (FrameIsCrv
					        ? (VoidPtr) Frame -> U.Crvs
					        : (VoidPtr) Frame -> U.Vec)
				     : NULL,
			    FrameIsCrv);

    if (SweepSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(SweepSrf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Constructs a Sweep surface out of the CrossSection curve, Axis curve and   M
* optional Scaling curve and Scaler which scales the CrossSection.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   CrossSection:  Cross section of sweep.                                   M
*   Axis:          Axis curve of sweep.                                      M
*   Scale:         Either a numeric constant scale or a scaling scalar curve.M
*   Frame:         Orientation specification. Either an orientation curve    M
*		   Or a vector specification.				     M
*   RRefine:       Refinement control.		                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A sweep surface.                                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenSweepScaleSrfObject                                                   M
*****************************************************************************/
IPObjectStruct *GenSweepScaleSrfObject(IPObjectStruct *CrossSection,
				       IPObjectStruct *Axis,
				       IPObjectStruct *Scale,
				       IPObjectStruct *Frame,
				       RealType *RRefine)
{
    IPObjectStruct *SrfObj;
    int FrameIsCrv = IP_IS_CRV_OBJ(Frame),
	HasFrame = FrameIsCrv || IP_IS_VEC_OBJ(Frame),
	Refine = REAL_PTR_TO_INT(RRefine);
    CagdCrvStruct
	*AxisCrv = Axis -> U.Crvs;
    CagdSrfStruct *SweepSrf;

    if (IP_IS_CRV_OBJ(Scale)) {
	CagdCrvStruct
	    *TCrv = CagdSweepAxisRefine(AxisCrv, Scale -> U.Crvs, Refine);

	AxisCrv = TCrv;
    }

    SweepSrf = CagdSweepSrf(CrossSection -> U.Crvs, AxisCrv,
			    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 (AxisCrv != Axis -> U.Crvs)
	CagdCrvFree(AxisCrv);

    if (SweepSrf == NULL)
	return NULL;

    SrfObj = GenSRFObject(SweepSrf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes an approximation to the offset of a (planar) curve or a surface.  M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:        Curve to approximate its offset by Offset amount.            M
*   Offset:     Amount of offset.                                            M
*   Tolerance:  Accuracy of offset.                                          M
*   BezInterp:  Do we want Bezier interpolation or approximation?            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    An offset approximating to Obj.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenOffsetObject                                                          M
*****************************************************************************/
IPObjectStruct *GenOffsetObject(IPObjectStruct *Obj,
				RealType *Offset,
				RealType *Tolerance,
				RealType *BezInterp)
{
    if (IP_IS_POLY_OBJ(Obj)) {
	IPPolygonStruct
	    *Pl = GMPolyOffset(Obj -> U.Pl, IP_IS_POLYGON_OBJ(Obj),
			       *Offset, NULL);
	IPObjectStruct 
	    *PlObj = GenPOLYObject(Pl);

    if (IP_IS_POLYGON_OBJ(Obj))
        IP_SET_POLYGON_OBJ(PlObj);
    else
        IP_SET_POLYLINE_OBJ(PlObj);

	return PlObj;
    }
    else if (IP_IS_SRF_OBJ(Obj)) {
    	IPObjectStruct *SrfObj;
    	CagdSrfStruct
	    *OffsetSrf = SymbSrfSubdivOffset(Obj -> U.Srfs, *Offset,
					     *Tolerance);

    	if (OffsetSrf == NULL)
	    return NULL;

    	SrfObj = GenSRFObject(OffsetSrf);

    	return SrfObj;
    }
    else if (IP_IS_TRIMSRF_OBJ(Obj)) {
    	IPObjectStruct *TSrfObj;
    	CagdSrfStruct
	    *OffsetSrf = SymbSrfSubdivOffset(Obj -> U.TrimSrfs -> Srf,
					     *Offset, *Tolerance);

    	if (OffsetSrf == NULL)
	    return NULL;

    	TSrfObj = GenTRIMSRFObject(TrimSrfNew(OffsetSrf,
			     TrimCrvCopyList(Obj -> U.TrimSrfs -> TrimCrvList),
					      TRUE));

    	return TSrfObj;
    }
    else if (IP_IS_CRV_OBJ(Obj)) {
    	IPObjectStruct *CrvObj;
    	CagdCrvStruct
	    *OffsetCrv = SymbCrvSubdivOffset(Obj -> U.Crvs, *Offset,
					     *Tolerance,
					     REAL_PTR_TO_INT(BezInterp));

    	if (OffsetCrv == NULL)
	    return NULL;

    	CrvObj = GenCRVObject(OffsetCrv);

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

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes an approximation to the offset of a (planar) curve or a surface.  M
*   This offset is computed and approximated to with a given tolerance       M
* Epsilon that is related to the square of the distance between the original M
* curve and its offset approximation.					     M
*   If Trim then regions in the curve with curvature that is larger than     M
* offset distance required will be trimmed out.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:        Curve to approximate its offset by Offset amount.            M
*   Offset:     Amount of offset.                                            M
*   Epsilon:    Accuracy of offset.                                          M
*   Trim:       Should we deal with and trim self intersecting loops?        M
*   BezInterp:  Do we want Bezier interpolation or approximation?            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    An offset approximating to Obj.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenAOffsetObject                                                         M
*****************************************************************************/
IPObjectStruct *GenAOffsetObject(IPObjectStruct *Obj,
				 RealType *Offset,
				 RealType *Epsilon,
				 RealType *Trim,
				 RealType *BezInterp)
{
    if (IP_IS_SRF_OBJ(Obj)) {
	IritNonFatalError("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) ?
	        SymbCrvAdapOffset(Obj -> U.Crvs, *Offset, *Epsilon,
				  NULL, REAL_PTR_TO_INT(BezInterp)) :
		SymbCrvAdapOffsetTrim(Obj -> U.Crvs, *Offset,
				      *Epsilon, NULL,
				      REAL_PTR_TO_INT(BezInterp));

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

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

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes an approximation to the offset of a (planar) curve or a surface.  M
*   This offset is computed and approximated using a least sqaure fit.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:          Curve to approximate its offset by Offset amount.          M
*   Offset:       Amount of offset.                                          M
*   NumOfSamples: To sample the offset curve.                                M
*   NumOfDOF:     Number of control points in resulting approximation.       M
*   Order:        Of resulting approximation.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    An offset approximating to Obj.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenLeastSqrOffsetObject                                                  M
*****************************************************************************/
IPObjectStruct *GenLeastSqrOffsetObject(IPObjectStruct *Obj,
					RealType *Offset,
					RealType *NumOfSamples,
					RealType *NumOfDOF,
					RealType *Order)
{
    CagdRType Tolerance;

    if (IP_IS_CRV_OBJ(Obj)) {
    	CagdCrvStruct
	    *OffsetCrv = SymbCrvLeastSquarOffset(Obj -> U.Crvs,
						 *Offset,
						 REAL_PTR_TO_INT(NumOfSamples),
						 REAL_PTR_TO_INT(NumOfDOF),
						 REAL_PTR_TO_INT(Order),
						 &Tolerance);

    	if (OffsetCrv == NULL)
	    return NULL;
	else
	    return GenCRVObject(OffsetCrv);
    }
    else {
	IritNonFatalError("LOffset allowed on curves only");
	return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes an approximation to the offset of a (planar) curve or a surface.  M
*   This offset is computed and approximated using a tangent field matcing.  M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:          Curve to approximate its offset by Offset amount.          M
*   Offset:       Amount of offset.                                          M
*   Tolerance:    Accuracy of offset, measured in offset direction (normal   M
*		  of curve) angular error, in radians.                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    An offset approximating to Obj.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GenMatchingOffsetObject                                                  M
*****************************************************************************/
IPObjectStruct *GenMatchingOffsetObject(IPObjectStruct *Obj,
					RealType *Offset,
					RealType *Tolerance)
{
    if (IP_IS_CRV_OBJ(Obj)) {
    	CagdCrvStruct
	    *OffsetCrv = SymbCrvMatchingOffset(Obj -> U.Crvs,
					       *Offset,
					       *Tolerance);

    	if (OffsetCrv == NULL)
	    return NULL;
	else
	    return GenCRVObject(OffsetCrv);
    }
    else {
	IritNonFatalError("MOffset allowed on curves only");
	return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Affine reparametrization, effectively changing the domain of the curve.    M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:         A curve to change its parametric domain.                    M
*   TMin, TMax:  New domain for Obj.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A curve identical to Obj but with parametric domain   M
*                      from TMin to TMax.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvReparametrization                                                     M
*****************************************************************************/
IPObjectStruct *CrvReparametrization(IPObjectStruct *Obj,
				     RealType *TMin,
				     RealType *TMax)
{
    IPObjectStruct *CrvObj;
    CagdCrvStruct
	*Crv = CAGD_IS_BEZIER_CRV(Obj -> U.Crvs) ?
			  CnvrtBezier2BsplineCrv(Obj -> U.Crvs) :
			  CagdCrvCopy(Obj -> U.Crvs);
    int Len = CAGD_CRV_PT_LST_LEN(Crv) + Crv -> Order;
    CagdRType MinDomain, MaxDomain,
	*KV = Crv -> KnotVector;

    CagdCrvDomain(Crv, &MinDomain, &MaxDomain);

    /* Translate to 0.0, scale, and translate to new location. */
    BspKnotAffineTrans(KV, Len, -MinDomain, 1.0);
    BspKnotScale(KV, Len, (*TMax - *TMin) / (MaxDomain - MinDomain));
    BspKnotAffineTrans(KV, Len, *TMin, 1.0);
    
    CrvObj = GenCRVObject(Crv);

    return CrvObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Affine reparametrization, effectively changing the domain of the surface.  M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:         A surface to change its parametric domain.                  M
*   RDir:        Direction of reparametrization. Either U or V.		     M
*   TMin, TMax:  New domain for Obj.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A surface identical to Obj but with parametric        M
*                      domain from TMin to TMax in direction RDir.           M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfReparametrization                                                     M
*****************************************************************************/
IPObjectStruct *SrfReparametrization(IPObjectStruct *Obj,
				     RealType *RDir,
				     RealType *TMin,
				     RealType *TMax)
{
    IPObjectStruct *SrfObj;
    CagdSrfDirType
	Dir = (CagdSrfDirType) REAL_PTR_TO_INT(RDir);
    CagdSrfStruct
	*Srf = CAGD_IS_BEZIER_SRF(Obj -> U.Srfs) ?
			  CnvrtBezier2BsplineSrf(Obj -> U.Srfs) :
			  CagdSrfCopy(Obj -> U.Srfs);
    int Len = Dir == CAGD_CONST_U_DIR
				? CAGD_SRF_UPT_LST_LEN(Srf) + Srf -> UOrder
				: CAGD_SRF_VPT_LST_LEN(Srf) + Srf -> VOrder;
    CagdRType MinDomain, MaxDomain,
	      MinUDomain, MaxUDomain, MinVDomain, MaxVDomain,
        *KV = Dir == CAGD_CONST_U_DIR ? Srf -> UKnotVector : Srf -> VKnotVector;

    CagdSrfDomain(Srf, &MinUDomain, &MaxUDomain, &MinVDomain, &MaxVDomain);
    if (Dir == CAGD_CONST_U_DIR) {
        MinDomain = MinUDomain;
	MaxDomain = MaxUDomain;
    }
    else {
        MinDomain = MinVDomain;
	MaxDomain = MaxVDomain;
    }

    /* Translate to 0.0, scale, and translate to new location. */
    BspKnotAffineTrans(KV, Len, -MinDomain, 1.0);
    BspKnotScale(KV, Len, (*TMax - *TMin) / (MaxDomain - MinDomain));
    BspKnotAffineTrans(KV, Len, *TMin, 1.0);

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Affine reparametrization, effectively changing the domain of the trivar.   M
*                                                                            *
* PARAMETERS:                                                                M
*   Obj:         A trivariate to change its parametric domain.               M
*   RDir:        Direction of reparametrization. Either U, V or W.	     M
*   TMin, TMax:  New domain for Obj.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A trivar identical to Obj but with parametric        M
*                      domain from TMin to TMax in direction RDir.           M
*                                                                            *
* KEYWORDS:                                                                  M
*   TrivReparametrization                                                    M
*****************************************************************************/
IPObjectStruct *TrivReparametrization(IPObjectStruct *Obj,
				      RealType *RDir,
				      RealType *TMin,
				      RealType *TMax)
{
    IPObjectStruct *TVObj;
    TrivTVDirType
	Dir = (TrivTVDirType) REAL_PTR_TO_INT(RDir);
    TrivTVStruct
	*TV = TRIV_IS_BEZIER_TV(Obj -> U.Trivars) ?
			  TrivCnvrtBezier2BsplineTV(Obj -> U.Trivars) :
			  TrivTVCopy(Obj -> U.Trivars);
    int Len;
    CagdRType *KV, MinDomain, MaxDomain, MinUDomain, MaxUDomain,
	MinVDomain, MaxVDomain, MinWDomain, MaxWDomain;

    TrivTVDomain(TV, &MinUDomain, &MaxUDomain,
		     &MinVDomain, &MaxVDomain,
		     &MinWDomain, &MaxWDomain);

    switch (Dir) {
        case TRIV_CONST_U_DIR:
            Len = TRIV_TV_UPT_LST_LEN(TV) + TV -> UOrder;
	    KV = TV -> UKnotVector;
	    MinDomain = MinUDomain;
	    MaxDomain = MaxUDomain;
	    break;
        case TRIV_CONST_V_DIR:
            Len = TRIV_TV_VPT_LST_LEN(TV) + TV -> VOrder;
	    KV = TV -> VKnotVector;
	    MinDomain = MinVDomain;
	    MaxDomain = MaxVDomain;
	    break;
        case TRIV_CONST_W_DIR:
            Len = TRIV_TV_WPT_LST_LEN(TV) + TV -> WOrder;
	    KV = TV -> WKnotVector;
	    MinDomain = MinWDomain;
	    MaxDomain = MaxWDomain;
	    break;
	default:
	    IritNonFatalError("TREPARAM: Wrong parametric direction.");
	    return NULL;
    }

    /* Translate to 0.0, scale, and translate to new location. */
    BspKnotAffineTrans(KV, Len, -MinDomain, 1.0);
    BspKnotScale(KV, Len, (*TMax - *TMin) / (MaxDomain - MinDomain));
    BspKnotAffineTrans(KV, Len, *TMin, 1.0);

    TVObj = GenTRIVARObject(TV);

    return TVObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes curvature properties of the given curve.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Curve to evaluate its curvature properties.                   M
*   Eps:       Accuracy of computation.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  Either extremum curvature locations if Eps > 0, or    M
*                      curvature field square curve if Eps < 0.              M
*                                                                            *
* KEYWORDS:                                                                  M
*   CrvCurvaturePts                                                          M
*****************************************************************************/
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) {
	IritNonFatalError(
	    "CCRVTR: Only 2 or 3 dimensional curves are supported.");
	return NULL;
    }

    if (*Eps <= 0.0) {
	CagdRType TMin, TMax;
	CagdCrvStruct *CrvtrCrv2D,
	    *CrvtrCrv = SymbCrv3DCurvatureSqr(PObj -> U.Crvs);

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

	NewPObj = GenCRVObject(CrvtrCrv2D);
    }
    else {
	int i;
	CagdPtStruct *IPtsTmp,
	    *IPts = SymbCrv2DExtremCrvtrPts(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
* Computes curvature properties of the given surface.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     Surface to compute curvature properties for.                   M
*   RPtType:  Point type of output.                                          M
*   RDir:     Either curvature in U or V or total upper bound.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A curvature bound field for surface PObj.            M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfCurvatureBounds                                                       M
*****************************************************************************/
IPObjectStruct *SrfCurvatureBounds(IPObjectStruct *PObj,
				   RealType *RPtType,
				   RealType *RDir)
{ 
    IPObjectStruct *NewPObj;
    CagdSrfStruct *TSrf, *CrvtrSrfBound;
    CagdPointType
	PtType = (CagdPointType) REAL_PTR_TO_INT(RPtType);
    CagdSrfDirType
	Dir = (CagdSrfDirType)  REAL_PTR_TO_INT(RDir);

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

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	case CAGD_CONST_V_DIR:
	    CrvtrSrfBound = SymbSrfIsoDirNormalCurvatureBound(PObj -> U.Srfs,
									Dir);
	    break;
	default:
	    CrvtrSrfBound = SymbSrfCurvatureUpperBound(PObj -> U.Srfs);
	    break;
    }

    switch (PtType) {
	case CAGD_PT_P1_TYPE:
	    break;
	case CAGD_PT_E1_TYPE:
	case CAGD_PT_E3_TYPE:
	case CAGD_PT_P3_TYPE:
	    TSrf = CagdCoerceSrfTo(CrvtrSrfBound, PtType);
	    CagdSrfFree(CrvtrSrfBound);
	    CrvtrSrfBound = TSrf;
	    break;
	default:
	    CagdSrfFree(CrvtrSrfBound);
	    IritNonFatalError("SCRVTR: Wrong point type coercion.");
	    return NULL;
    }

    NewPObj = GenSRFObject(CrvtrSrfBound);

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the Gauss curvature surface of the given surface.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     Surface to compute its gauss curvature surface for.            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A Gauss curvature surface of PObj.                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfGaussCurvature                                                        M
*****************************************************************************/
IPObjectStruct *SrfGaussCurvature(IPObjectStruct *PObj)
{ 
    IPObjectStruct *NewPObj;
    CagdSrfStruct *GaussSrf;

    if (CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) < 2 ||
	CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) > 3) {
	IritNonFatalError(
	    "SGauss: Only 2 or 3 dimensional surfaces are supported.");
	return NULL;
    }

    GaussSrf = SymbSrfGaussCurvature(PObj -> U.Srfs);

    NewPObj = GenSRFObject(GaussSrf);

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the Mean curvature square surface of the given surface.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     Surface to compute its mean curvature square surface for.      M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A Mean curvature square surface of PObj.             M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfMeanSqrCurvature                                                      M
*****************************************************************************/
IPObjectStruct *SrfMeanSqrCurvature(IPObjectStruct *PObj)
{ 
    IPObjectStruct *NewPObj;
    CagdSrfStruct *MeanSrf;

    if (CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) < 2 ||
	CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) > 3) {
	IritNonFatalError(
	    "SMeanSqr: Only 2 or 3 dimensional surfaces are supported.");
	return NULL;
    }

    MeanSrf = SymbSrfMeanCurvatureSqr(PObj -> U.Srfs);

    NewPObj = GenSRFObject(MeanSrf);

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the isoparametric focal surface of the given surface.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     Surface to compute its focal surface using isoparametric	     M
*	      normal curvature direction.				     M
*   RDir:     Either iso focal surface in U or V.		             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   An iso focal surface of surface PObj.                M
*                                                                            *
* KEYWORDS:                                                                  M
*   SrfIsoFocalSrf                                                        M
*****************************************************************************/
IPObjectStruct *SrfIsoFocalSrf(IPObjectStruct *PObj, RealType *RDir)
{ 
    IPObjectStruct *NewPObj;
    CagdSrfStruct *IsoFocalSrf;
    CagdSrfDirType
	Dir = (CagdSrfDirType) REAL_PTR_TO_INT(RDir);

    if (CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) < 2 ||
	CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) > 3) {
	IritNonFatalError(
	    "IsoFocal: Only 2 or 3 dimensional surfaces are supported.");
	return NULL;
    }

    IsoFocalSrf = SymbSrfIsoFocalSrf(PObj -> U.Srfs, Dir);

    NewPObj = GenSRFObject(IsoFocalSrf);

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes the (mean) evolute curve/surface of the given curve/surface.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     Curve/Surface to compute its (mean) evolute for.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   A (mean) evolute curve/surface of PObj.              M
*                                                                            *
* KEYWORDS:                                                                  M
*   FreeformEvolute                                                          M
*****************************************************************************/
IPObjectStruct *FreeformEvolute(IPObjectStruct *PObj)
{ 
    IPObjectStruct *NewPObj;

    if (IP_IS_CRV_OBJ(PObj)) {
	CagdCrvStruct *EvoluteCrv, *EvoluteCrvAux;

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

	EvoluteCrvAux = SymbCrv3DRadiusNormal(PObj -> U.Crvs);
	EvoluteCrv = SymbCrvAdd(PObj -> U.Crvs, EvoluteCrvAux);
	CagdCrvFree(EvoluteCrvAux);

	NewPObj = GenCRVObject(EvoluteCrv);
    }
    else if (IP_IS_SRF_OBJ(PObj)) {
	CagdSrfStruct *EvoluteSrf, *EvoluteSrfAux;

	if (CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) < 2 ||
	    CAGD_NUM_OF_PT_COORD(PObj -> U.Srfs -> PType) > 3) {
	    IritNonFatalError(
		"EVOLUTE: Only 2 or 3 dimensional surfaces are supported.");
	    return NULL;
	}

	EvoluteSrfAux = SymbSrfMeanEvolute(PObj -> U.Srfs);
	EvoluteSrf = SymbSrfAdd(PObj -> U.Srfs, EvoluteSrfAux);
	CagdSrfFree(EvoluteSrfAux);

	NewPObj = GenSRFObject(EvoluteSrf);
    }

    return NewPObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Merges two surfaces into one in specified Dir and SameEdge flag. 	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf1, Srf2:  Two surfaces to merge.                                      M
*   Dir:         Direction of merge. Either U or V.                          M
*   SameEdge:    Do Srf1 and Srf2 share a common edge?                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  A surface result of the merge.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   MergeSrfSrf                                                              M
*****************************************************************************/
IPObjectStruct *MergeSrfSrf(IPObjectStruct *Srf1,
			    IPObjectStruct *Srf2,
			    RealType *Dir,
			    RealType *SameEdge)
{
    IPObjectStruct *SrfObj;
    CagdSrfStruct
	*Srf = CagdMergeSrfSrf(Srf1 -> U.Srfs, Srf2 -> U.Srfs,
			       (CagdSrfDirType) REAL_PTR_TO_INT(Dir),
			       REAL_PTR_TO_INT(SameEdge), TRUE);

    SrfObj = GenSRFObject(Srf);

    return SrfObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Merge two curves/ctl points into one curve by adding a linear segment      M
* between the first end point to second start point.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1, PObj2:  Either curve or a control point to merge.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Merged curve.                                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   MergeCurvesAndCtlPoints                                                  M
*****************************************************************************/
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, TRUE);
	}
	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;
}
