/*****************************************************************************
* Program to create illustrations of wireframe drawings.		     *
*   Usually the output of this program is piped to irit2ps, although it      *
* creates a regular IRIT data files with polylines.			     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, June 1993   *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "program.h"
#include "config.h"
#include "iritgrap.h"
#include "allocate.h"
#include "poly_cln.h"
#include "geomat3d.h"
#include "getarg.h"
#include "ip_cnvrt.h"

#define HEAT_CRV_NUM_PTS	9

#ifdef NO_CONCAT_STR
static char *VersionStr =
    "Illustrt		Version 5.0,	Gershon Elber,\n\
	 (C) Copyright 1989/90-95 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr =
    "Illustrt		" VERSION ",	Gershon Elber,	"
	__DATE__ ",   " __TIME__ "\n" COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char *CtrlStr =
#ifdef DOUBLE
    "Illustrt I%-#IsoLines!d f%-PolyOpti|SampPerCrv!d!d s%- M%- P%- p%- l%-MaxLnLen!F a%- t%-TrimInter!F o%-OutName!s Z%-InterSameZ!F m%- z%- DFiles!*s";
#else
    "Illustrt I%-#IsoLines!d f%-PolyOpti|SampPerCrv!d!d s%- M%- P%- p%- l%-MaxLnLen!f a%- t%-TrimInter!f o%-OutName!s Z%-InterSameZ!f m%- z%- DFiles!*s";
#endif

static char
    *GlblOutFileName = "illustrt.dat";

static int
    GlblTalkative = FALSE,
    GlblSortOutput = FALSE,
    GlblPolylineOptiApprox = 0,
    GlblDrawSurfaceMesh = FALSE,
    GlblDrawSurface = TRUE,
    GlblNumOfIsolines = IG_DEFAULT_NUM_OF_ISOLINES,
    GlblSamplesPerCurve = IG_DEFAULT_SAMPLES_PER_CURVE;

static RealType
    GlblMaxLineLen = DEFAULT_MAX_LINE_LEN;

int GlblAngularDistance = TRUE,
    GlblVertexPoints = FALSE,
    GlblSplitLongLines = FALSE;

RealType
    GlblInterSameZ = INTER_SAME_Z,
    GlblTrimIntersect = DEFAULT_TRIM_INTERSECT;

static MatrixType CrntViewMat;			/* This is the current view! */

static ConfigStruct SetUp[] =
{
  { "NumOfIsolines",  "-I", (VoidPtr) &GlblNumOfIsolines,    SU_INTEGER_TYPE },
  { "PolyOpti",       "-f", (VoidPtr) &GlblPolylineOptiApprox,SU_INTEGER_TYPE },
  { "SamplesPerCurve","-f", (VoidPtr) &GlblSamplesPerCurve,  SU_INTEGER_TYPE },
  { "SortOutput",     "-s", (VoidPtr) &GlblSortOutput,	     SU_BOOLEAN_TYPE },
  { "DrawSurfaceMesh","-M", (VoidPtr) &GlblDrawSurfaceMesh,  SU_BOOLEAN_TYPE },
  { "DrawSurface",    "-P", (VoidPtr) &GlblDrawSurface,	     SU_BOOLEAN_TYPE },
  { "VertexPoints",   "-p", (VoidPtr) &GlblVertexPoints,     SU_BOOLEAN_TYPE },
  { "AngularDist",    "-a", (VoidPtr) &GlblAngularDistance,  SU_BOOLEAN_TYPE },
  { "MoreVerbose",    "-m", (VoidPtr) &GlblTalkative,	     SU_BOOLEAN_TYPE },
  { "InterSameZ",     "-Z", (VoidPtr) &GlblInterSameZ,	     SU_REAL_TYPE }
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

static void DumpData(char *FileName,
		     IPObjectStruct *NoProcessObjs,
		     IPObjectStruct *PObjects);
static IPObjectStruct *ProcessSpeedWave(IPObjectStruct *PObj,
					char *SpeedWaveAttrs);
static IPPolygonStruct *GenSpeedWave(RealType Coords[3],
				     RealType GenRand,
				     RealType Dir[3],
				     RealType Len,
				     RealType Dist,
				     RealType LenRand,
				     RealType DistRand);
static IPObjectStruct *ProcessHeatWave(IPObjectStruct *PObj,
				       char *HeatWaveAttrs);
static CagdCrvStruct *GenHeatWave(RealType Coords[3],
				  RealType GenRand,
				  RealType Len,
				  RealType Dist,
				  RealType LenRand,
				  RealType DistRand);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of illustrt - Read command line and do what is needed...	     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:  Command line.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   main                                                                     M
*****************************************************************************/
void main(int argc, char **argv)
{
    int Error,
	NumOfIsolinesFlag = FALSE,
	CrvOptiPolylinesFlag = FALSE,
        TrimInterFlag = FALSE,
	OutFileFlag = FALSE,
	InterSameZFlag = FALSE,
	VerFlag = FALSE,
	NumFiles = 0;
    char
	**FileNames = NULL;
    IPObjectStruct *PObjects, *NoProcessObjs, *PObj,
	*PObjWaves = NULL;

    Config("illustrt", SetUp, NUM_SET_UP);   /* Read config. file if exists. */

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
			   &NumOfIsolinesFlag, &GlblNumOfIsolines,
			   &CrvOptiPolylinesFlag, &GlblPolylineOptiApprox,
			   &GlblSamplesPerCurve,
			   &GlblSortOutput, &GlblDrawSurfaceMesh,
			   &GlblDrawSurface, &GlblVertexPoints,
			   &GlblSplitLongLines, &GlblMaxLineLen,
			   &GlblAngularDistance,
			   &TrimInterFlag, &GlblTrimIntersect,
			   &OutFileFlag, &GlblOutFileName,
			   &InterSameZFlag, &GlblInterSameZ, &GlblTalkative,
			   &VerFlag, &NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	IllustrateExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	ConfigPrint(SetUp, NUM_SET_UP);
	IllustrateExit(0);
    }

    if (!NumFiles) {
	fprintf(stderr, "No data file names where given, exit.\n");
	GAPrintHowTo(CtrlStr);
	IllustrateExit(1);
    }

    /* Get the data files: */
    if ((PObjects = IritPrsrGetDataFiles(FileNames, NumFiles, TRUE, FALSE)) ==
									NULL)
	IllustrateExit(0);

    if (IritPrsrWasPrspMat)
	MatMultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat);
    else
	GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType));

    /* Scan the objects for wave supported attributes. */
    for (PObj = PObjects; PObj != NULL; PObj = PObj -> Pnext) {
	IPObjectStruct *PObjWave;
	char *p;

	if ((p = AttrGetObjectStrAttrib(PObj, "speedwave")) != NULL &&
	    (PObjWave = ProcessSpeedWave(PObj, p)) != NULL) {
	    PObjWave -> Pnext = PObjWaves;
	    PObjWaves = PObjWave;
	}
	if ((p = AttrGetObjectStrAttrib(PObj, "heatwave")) != NULL &&
	    (PObjWave = ProcessHeatWave(PObj, p)) != NULL) {
	    PObjWave -> Pnext = PObjWaves;
	    PObjWaves = PObjWave;
	}
    }

    PObj = GMTransformObjectList(PObjects, CrntViewMat);
    IPFreeObjectList(PObjects);
    PObjects = PObj;

    if (PObjWaves != NULL) {
	PObj = GMTransformObjectList(PObjWaves, CrntViewMat);
	IPFreeObjectList(PObjWaves);
	PObjWaves = PObj;
    }

    for (PObj = PObjects, PObjects = NoProcessObjs = NULL; PObj != NULL; ) {
	IPObjectStruct
	    *PObjNext = PObj -> Pnext;

	if (AttrGetObjectStrAttrib(PObj, "IllustrtNoProcess") != NULL) {
	    PObj -> Pnext = NoProcessObjs;
	    NoProcessObjs = PObj;
	}
	else {
	    PObj -> Pnext = PObjects;
	    PObjects = PObj;
	}

	PObj = PObjNext;
    }

    if (GlblVertexPoints) {
	/* Add vertices of each polyline as points into data set. */
	for (PObj = PObjects; PObj != NULL;) {
	    if (IP_IS_POLY_OBJ(PObj)) {
		IPObjectStruct
		    *PtList = CopyObject(NULL, PObj, TRUE);

		IP_SET_POINTLIST_OBJ(PtList);

		RemoveInternalVertices(PtList);

		PObj -> Pnext = PtList;
		PObj = PtList -> Pnext;
	    }
	    else
		PObj = PObj -> Pnext;
	}
    }

    ProcessIntersections(PObjects);
    if (GlblSplitLongLines)
	SplitLongLines(PObjects, GlblMaxLineLen);

    if (GlblSortOutput)
	SortOutput(&PObjects);

    /* Append the objects created to represent waves. */
    PObj = IritPrsrGetLastObj(PObjects);
    PObj -> Pnext = PObjWaves;

    DumpData(OutFileFlag ? GlblOutFileName : NULL, NoProcessObjs, PObjects);

    IllustrateExit(0);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert all surfaces/curves into polylines as follows:	     M
*   Curve is converted to a single polyline with SamplesPerCurve samples.    M
*   Surface is converted into GlblNumOfIsolines curves in each axes, each    M
* handled as Curve above. The original curves and surfaces are then deleted. M
*   This function is a call back function of the irit parser.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForms:  Crvs/Srfs/Trimmed Srfs/Trivariates read from a file by the   M
*               irit parser.					             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Processed freeform geometry. This function simply    M
*                       returns what it gots.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrProcessFreeForm, conversion                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IritPrsrFreeFormStruct *FreeForms)
{
    CagdCrvStruct *Crv, *Crvs;
    CagdSrfStruct *Srf, *Srfs;
    IPObjectStruct *PObj,
	*CrvObjs = FreeForms -> CrvObjs,
	*SrfObjs = FreeForms -> SrfObjs;
    IPPolygonStruct *PPolygon, *PPolygonTemp;

    if (CrvObjs == NULL && SrfObjs == NULL)
	return NULL;

    /* Make sure requested format is something reasonable. */
    if (GlblNumOfIsolines < 2) {
	GlblNumOfIsolines = 2;
	fprintf(stderr, "NumOfIsolines is less than 2, 2 picked instead.\n");
    }

    if (GlblSamplesPerCurve < 2) {
	GlblSamplesPerCurve = 2;
	fprintf(stderr,
		"SamplesPerCurve is less than 2, 2 picked instead.\n");
    }

    if (CrvObjs) {
	for (PObj = CrvObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblTalkative)
		fprintf(stderr, "Processing curve object \"%s\"\n",
			PObj -> Name);

	    Crvs = PObj -> U.Crvs;
	    PObj -> U.Pl = NULL;
	    PObj -> ObjType = IP_OBJ_POLY;
	    IP_SET_POLYLINE_OBJ(PObj);
	    for (Crv = Crvs; Crv != NULL; Crv = Crv -> Pnext) {
		PPolygon = PPolygonTemp =
		    IritCurve2Polylines(Crv, GlblSamplesPerCurve,
					GlblPolylineOptiApprox);

		if (PPolygon != NULL) {
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = PPolygon;
		}
	    }
	    CagdCrvFreeList(Crvs);
	}
    }

    if (SrfObjs) {
	int NumOfIso[2];

	NumOfIso[0] = -GlblNumOfIsolines;
	NumOfIso[1] = -GlblNumOfIsolines;

	for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblTalkative)
		fprintf(stderr, "Processing surface object \"%s\"\n",
			PObj -> Name);
	    
	    Srfs = PObj -> U.Srfs;
	    PObj -> U.Pl = NULL;
	    PObj -> ObjType = IP_OBJ_POLY;
	    IP_SET_POLYLINE_OBJ(PObj);
	    for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
		PPolygon = PPolygonTemp =
		    IritSurface2Polylines(Srf, NumOfIso,
					  GlblSamplesPerCurve,
					  GlblPolylineOptiApprox);

		if (PPolygon != NULL) {
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = PPolygon;
		}
	    }
	    CagdSrfFreeList(Srfs);
	}
    }

    if (SrfObjs == NULL)
	return CrvObjs;
    else if (CrvObjs == NULL)
	return SrfObjs;
    else {
	for (PObj = SrfObjs; PObj -> Pnext != NULL; PObj = PObj -> Pnext);
	PObj -> Pnext = CrvObjs;
	return SrfObjs;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps the data out into FileName (stdout in NULL).			     *
*                                                                            *
* PARAMETERS:                                                                *
*   FileName:        Where output should go to.                              *
*   NoProcessObjs:   List of object that are piped through from input stream *
*		     unprocessed.                                            *
*   PObjects:        List of processed object to dump out.                   *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpData(char *FileName,
		     IPObjectStruct *NoProcessObjs,
		     IPObjectStruct *PObjects)
{
    FILE *f;

    if (FileName != NULL) {
	if ((f = fopen(FileName, "w")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", FileName);
	    IllustrateExit(2);
	}
    }
    else
	f = stdout;

    fprintf(f, "\tIrit Solid Modeller Data File (ILLUSTRT), %s\n\n",
	    IritRealTimeDate());

    while (PObjects) {
	/* Dump only polys with at least two vertices and points/vectors. */
	if (IP_IS_POLY_OBJ(PObjects)) {
	    if (IP_IS_POLYLINE_OBJ(PObjects))
		CleanUpPolylineList(&PObjects -> U.Pl);
	    if (PObjects -> U.Pl != NULL)
		IritPrsrPutObjectToFile(f, PObjects);
	}
	else if (IP_IS_POINT_OBJ(PObjects) ||
		 IP_IS_VEC_OBJ(PObjects) ||
		 IP_IS_CRV_OBJ(PObjects)) {
	    IritPrsrPutObjectToFile(f, PObjects);
	}

	PObjects = PObjects -> Pnext;
    }

    while (NoProcessObjs) {
	IritPrsrPutObjectToFile(f, NoProcessObjs);

	NoProcessObjs = NoProcessObjs -> Pnext;
    }

    fclose(f);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Creates an object representing speed wave from object PObj. SpeedWaveAttr  *
* Holds the attributes of the speed wave in the following format:	     *
* "GenRand,DirX,DirY,DirZ,Len,Dist,LenRand,DistRand,Width" with		     *
* See GenSpeedWave below.						     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:               Object for which a speed wave it to be generated.    *
*   SpeedWaveAttrs:     String describing a speed wave.                      *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:   Object representing a speed wave.		     *
*****************************************************************************/
static IPObjectStruct *ProcessSpeedWave(IPObjectStruct *PObj,
					char *SpeedWaveAttrs)
{
    int i, NumPoints;
    char StrLineWidth[LINE_LEN];
    RealType **Points, GenRand, Dir[3], Len, Dist, LenRand, DistRand, Width;
    CagdSrfStruct
	*Srf = NULL;
    CagdCrvStruct
	*Crv = NULL;
    IPObjectStruct *PObjTmp,  *PObjWave;
    IPPolygonStruct *P, *PWave;

#ifdef DOUBLE
    if (sscanf(SpeedWaveAttrs, "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
#else
    if (sscanf(SpeedWaveAttrs, "%f,%f,%f,%f,%f,%f,%f,%f,%f",
#endif /* DOUBLE */
	       &GenRand, &Dir[0], &Dir[1], &Dir[2], &Len,
	       &Dist, &LenRand, &DistRand, &Width) != 9) {
	fprintf(stderr, "Wrong parameters of speed wave. Ignored.\n");
	return NULL;
    }

    PObjWave = IPAllocObject("SpeedWave", IP_OBJ_POLY, NULL);
    sprintf(StrLineWidth, "%f", Width);
    AttrSetObjectStrAttrib(PObjWave, "width", StrLineWidth);
    IP_SET_POLYLINE_OBJ(PObjWave);

    switch (PObj -> ObjType) {
	case IP_OBJ_LIST_OBJ:
	    for (i = 0; (PObjTmp = ListObjectGet(PObj, i)) != NULL; i++)
		ProcessSpeedWave(PObjTmp, SpeedWaveAttrs);
	    break;
	case IP_OBJ_POLY:
	    for (P = PObj -> U.Pl; P != NULL; P = P -> Pnext) {
		IPVertexStruct
		    *V = P -> PVertex;

		do {
		    if ((PWave = GenSpeedWave(V -> Coord, GenRand, Dir, Len,
					  Dist, LenRand, DistRand)) != NULL) {
			PWave -> Pnext = PObjWave -> U.Pl;
			PObjWave -> U.Pl = PWave;
		    }

		    V = V -> Pnext;
		}
		while (V != NULL && V != P -> PVertex);
	    }
	    break;
	case IP_OBJ_SURFACE:
	case IP_OBJ_CURVE:
	    if (PObj -> ObjType == IP_OBJ_SURFACE) {
		Srf = CagdCoerceSrfTo(PObj -> U.Srfs, CAGD_PT_E3_TYPE);

		Points = Srf -> Points;
		NumPoints = Srf -> ULength * Srf -> VLength;
	    }
	    else {
		Crv = CagdCoerceCrvTo(PObj -> U.Crvs, CAGD_PT_E3_TYPE);

		Points = Crv -> Points;
		NumPoints = Crv -> Length;
	    }

	    for (i = 0; i < NumPoints; i++) {
		int j;
		RealType Coords[3];

		for (j = 0; j < 3; j++)
		    Coords[j] = Points[j + 1][i];

		if ((PWave = GenSpeedWave(Coords, GenRand, Dir, Len,
					  Dist, LenRand, DistRand)) != NULL) {
		    PWave -> Pnext = PObjWave -> U.Pl;
		    PObjWave -> U.Pl = PWave;
		}
	    }

	    if (PObj -> ObjType == IP_OBJ_SURFACE)
		CagdSrfFree(Srf);
	    else
		CagdCrvFree(Crv);
	    break;
	default:
	    break;
    }

    if (PObjWave -> U.Pl != NULL)
	return PObjWave;
    else {
	IPFreeObject(PObjWave);
	return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Randomly generates a single speed wave instance.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Coords:             Location for which the wave is to be generated.      *
*   GenRand:            Probability of generating the wave.		     *
*   Dir:                Direction of wave.				     *
*   Len, Dist:          Length of the wave and distance from object.	     *
*   LenRand, DistRand:  Pertubation amount Len and Dist.		     *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPPolygonStruct *:  An object representing one speed wave object         *
*                       in probability GenRand, NULL otherwise.              *
*****************************************************************************/
static IPPolygonStruct *GenSpeedWave(RealType Coords[3],
				     RealType GenRand,
				     RealType Dir[3],
				     RealType Len,
				     RealType Dist,
				     RealType LenRand,
				     RealType DistRand)
{
    VectorType DirCopy;
    IPVertexStruct *V1, *V2;
    IPPolygonStruct *P;

    /* Test if probability is in favor. */
    if (IritRandom(0.0, 1.0) > GenRand)
	return NULL;

    V2 = IPAllocVertex(0, 0, NULL, NULL);
    V1 = IPAllocVertex(0, 0, NULL, V2);
    P = IPAllocPolygon(0, 0, V1, NULL);

    PT_COPY(DirCopy, Dir);
    PT_NORMALIZE(DirCopy);
    Dist += DistRand * IritRandom(-1.0, 1.0);
    PT_SCALE(DirCopy, Dist);
    PT_COPY(V1 -> Coord, Coords);
    PT_ADD(V1 -> Coord, V1 -> Coord, DirCopy);

    PT_COPY(DirCopy, Dir);
    PT_NORMALIZE(DirCopy);
    Len += LenRand * IritRandom(-1.0, 1.0);
    PT_SCALE(DirCopy, Len);
    PT_COPY(V2 -> Coord, V1 -> Coord);
    PT_ADD(V2 -> Coord, V2 -> Coord, DirCopy);

    return P;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Creates an object representing heat wave from object PObj. HeatWaveAttr    *
* Holds the attributes of the heat wave in the following format:	     *
* "GenRand,Len,Dist,LenRand,DistRand,Width" with			     *
* See GenHeatWave below.						     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:               Object for which a heat wave it to be generated.     *
*   SpeedWaveAttrs:     String describing a heat wave.                       *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:   Object representing a heat wave.		     *
*****************************************************************************/
static IPObjectStruct *ProcessHeatWave(IPObjectStruct *PObj,
				       char *HeatWaveAttrs)
{
    int i, NumPoints;
    RealType **Points, GenRand, Len, Dist, LenRand, DistRand, Width;
    CagdSrfStruct
	*Srf = NULL;
    CagdCrvStruct *PWave,
	*Crv = NULL;
    IPObjectStruct *PObjTmp, *PObjWave;
    IPPolygonStruct *P;

#ifdef DOUBLE
    if (sscanf(HeatWaveAttrs, "%lf,%lf,%lf,%lf,%lf,%lf",
#else
    if (sscanf(HeatWaveAttrs, "%f,%f,%f,%f,%f,%f",
#endif /* DOUBLE */
	       &GenRand, &Len, &Dist, &LenRand, &DistRand, &Width) != 6) {
	fprintf(stderr, "Wrong parameters of heat wave. Ignored.\n");
	return NULL;
    }

    PObjWave = IPAllocObject("HeatWave", IP_OBJ_CURVE, NULL);
    AttrSetObjectRealAttrib(PObjWave, "width", Width);

    switch (PObj -> ObjType) {
	case IP_OBJ_LIST_OBJ:
	    for (i = 0; (PObjTmp = ListObjectGet(PObj, i)) != NULL; i++)
		ProcessHeatWave(PObjTmp, HeatWaveAttrs);
	    break;
	case IP_OBJ_POLY:
	    for (P = PObj -> U.Pl; P != NULL; P = P -> Pnext) {
		IPVertexStruct
		    *V = P -> PVertex;

		do {
		    if ((PWave = GenHeatWave(V -> Coord, GenRand, Len,
					  Dist, LenRand, DistRand)) != NULL) {
			PWave -> Pnext = PObjWave -> U.Crvs;
			PObjWave -> U.Crvs = PWave;
		    }

		    V = V -> Pnext;
		}
		while (V != NULL && V != P -> PVertex);
	    }
	    break;
	case IP_OBJ_SURFACE:
	case IP_OBJ_CURVE:
	    if (PObj -> ObjType == IP_OBJ_SURFACE) {
		Srf = CagdCoerceSrfTo(PObj -> U.Srfs, CAGD_PT_E3_TYPE);

		Points = Srf -> Points;
		NumPoints = Srf -> ULength * Srf -> VLength;
	    }
	    else {
		Crv = CagdCoerceCrvTo(PObj -> U.Crvs, CAGD_PT_E3_TYPE);

		Points = Crv -> Points;
		NumPoints = Crv -> Length;
	    }

	    for (i = 0; i < NumPoints; i++) {
		int j;
		RealType Coords[3];

		for (j = 0; j < 3; j++)
		    Coords[j] = Points[j + 1][i];

		if ((PWave = GenHeatWave(Coords, GenRand, Len,
					 Dist, LenRand, DistRand)) != NULL) {
		    PWave -> Pnext = PObjWave -> U.Crvs;
		    PObjWave -> U.Crvs = PWave;
		}
	    }

	    if (PObj -> ObjType == IP_OBJ_SURFACE)
		CagdSrfFree(Srf);
	    else
		CagdCrvFree(Crv);
	    break;
	default:
	    break;
    }

    if (PObjWave -> U.Pl != NULL)
	return PObjWave;
    else {
	IPFreeObject(PObjWave);
	return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Randomly generates a single heat wave instance.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Coords:             Location for which the wave is to be generated.      *
*   GenRand:            Probability of generating the wave.		     *
*   Len, Dist:          Length of the wave and distance from object.	     *
*   LenRand, DistRand:  Pertubation amount Len and Dist.		     *
*                                                                            *
* RETURN VALUE:                                                              *
*   CagdCrvStruct *:    An object representing one heat wave object          *
*                       in probability GenRand, NULL otherwise.              *
*****************************************************************************/
static CagdCrvStruct *GenHeatWave(RealType Coords[3],
				  RealType GenRand,
				  RealType Len,
				  RealType Dist,
				  RealType LenRand,
				  RealType DistRand)
{
    static VectorType
	HeatCtlPts[HEAT_CRV_NUM_PTS] =
	{
	    { 0.0, 0.0, 0.0 },
	    { 0.1, 0.0, 0.25 },
	    { 0.0, 0.1, 0.3 },
	    { 0.0, 0.0, 0.2 },
	    { 0.1, 0.0, 0.35 },
	    { 0.0, 0.0, 0.5 },
	    { 0.0, 0.0, 0.35 },
	    { 0.1, 0.1, 0.5 },
	    { 0.1, 0.0, 0.6 },
	};
    int i, j;
    CagdCrvStruct *Crv;

    /* Test if probability is in favor. */
    if (IritRandom(0.0, 1.0) > GenRand)
	return NULL;

    Len += LenRand * IritRandom(-1.0, 1.0);
    Dist += DistRand * IritRandom(-1.0, 1.0);

    Crv = BspCrvNew(HEAT_CRV_NUM_PTS, 3, CAGD_PT_E3_TYPE);
    BspKnotUniformOpen(HEAT_CRV_NUM_PTS, 3, Crv -> KnotVector);
    for (i = 0; i < HEAT_CRV_NUM_PTS; i++)
	for (j = 0; j < 3; j++)
	    Crv -> Points[j + 1][i] = Coords[j] + (j == 2 ? Dist : 0.0) + 
		HeatCtlPts[i][j] * Len;

    return Crv;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Illustrt Exit routine.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   ExitCode:    To notify O.S. with result of program.                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IllustrateExit                                                           M
*****************************************************************************/
void IllustrateExit(int ExitCode)
{
    exit(ExitCode);
}

#ifdef DEBUG

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Dummy function to link at debugging time.                               *
*                                                                            *
* PARAMETERS:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*****************************************************************************/
void DummyLinkCagdDebug(void)
{
    IritPrsrDbg();
}

#endif /* DEBUG */
