#pragma ident "@(#)types.c	1.2 98/08/25 SMI"

/*
 * types.c
 *
 *	@(#)types.c 1.1 96/06/17 21:54:22
 *
 * Copyright (c) 1996, Sun Microsystems Computer Company, Mountain View, CA
 *
 *			All Rights Reserved
 *
 *	This is a program for manipulating 3D objects.  It is currently being
 *	developed by Mike M. Chow and Scott R. Nelson. 
 *
 *
 *      This module implements an object system for geometry objects such
 *      as vertex, edge, and facet.  The object system has methods,
 *      constructors, destructors for each object.  All ModelViewer functions
 *      call these methods and constructors when dealing with geometry types.
 *    
 *      For details on geometry data structures, see types.h
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "types.h"
#include "zdebug.h"
#include "utils.h"


/* This variable should depend on an object's bbox range (future). */
#define FLOAT_EQUAL_THRES 0.001        /* How close can numbers be. */ 
#define MIN_OBJ_PART_CNT 10            /* Average parts per object. */
#define MIN_VERTS_PER_OBJ 500          /* Average vertices per object. */
#define MIN_VERTS_PER_FACET 4          /* By far, the most common
					  one is quad. */

#define minf(x, y)  (x < y) ? x : y
#define maxf(x, y)  (x > y) ? x : y


static double identityMat[16] = {1.0, 0.0, 0.0, 0.0,
				  0.0, 1.0, 0.0, 0.0,
				  0.0, 0.0, 1.0, 0.0,
				  0.0, 0.0, 0.0, 1.0};


/* Point Methods */

/*
 * pointEqual
 *
 *      Check if two points are very similar. In the future, change
 *      this so FLOAT_EQUAL_THRES depends on bbox range.
 */

int 
pointEqual(Point p1, Point p2)
{
    int result;
    result =((fabs(p1[0] - p2[0]) < FLOAT_EQUAL_THRES) &&
	    (fabs(p1[1] - p2[1]) < FLOAT_EQUAL_THRES) &&
	    (fabs(p1[2] - p2[2]) < FLOAT_EQUAL_THRES));
    zdo6 fprintf(stderr, "result:%d\n", result);
    return result;
}  /* End of pointEqual*/

	    
/*
 * printPoint
 *
 *      Pretty point printer.
 */

void 
printPoint(Point p)
{
    printf("%f %f %f\n", p[0], p[1], p[2]);
}  /* End of printPoint*/


/*
 * pointPrint
 *
 *      Prints a point to a file pointer.
 */

void
pointPrint(FILE *fp, int indent, float c[])
{
  int i;

  spaces(fp, indent);
  for (i = 0; i < 3; i++)
    fprintf(fp, "%f ", c[i]);
  fprintf(fp, "\n");
}  /* End of pointPrint*/




/*
 * PointCopy
 *
 *      Copy point, p2 into p1.      
 */

void 
pointCopy(Point p1, Point p2)
{
    p1[0] = p2[0];
    p1[1] = p2[1];
    p1[2] = p2[2];
}  /* End of PointCopy*/



/* Matrix Methods */


/*
 * matrixCopy
 *
 *      Copies matrix m2 into m1.
 */

void
matrixCopy(Matrix m1, Matrix m2)
{
  int i;
  
  for (i = 0; i < 16; i++)
    m1[i] = m2[i];
}  /* End of MatrixCopy*/




/*
 * matrixMakeIdentity
 *
 *      Copies the identity matrix into m. 
 */

void
matrixMakeIdentity(Matrix m)
{
  matrixCopy(m, identityMat);
}  /* End of matrixMakeIdentity */




/* Vertex Methods */
/*
 * vertexInit
 *
 *      Routine to init a new Vertex.
 */

void *
vertexInit (Vertex *vptr)
{    
    vptr->facets = flistNewNode();
    vptr->elist = linkedListNewNode();
    vptr->owner = NULL;
    vptr->flags = 0;
    vptr->radius = -1.0;
    vptr->boundaryVertex = 0;
    vptr->normal[0] = 0.0;
    vptr->normal[1] = 0.0;
    vptr->normal[2] = 1.0;
    vptr->surf = NULL;
    return NULL;
}  /* End of vertexInit*/

/*
 * vertexNew
 *
 *      Routine to create a new Vertex.
 */

Vertex *
vertexNew ()
{
    Vertex *vptr;
    
    vptr = (Vertex *) malloc(sizeof(Vertex));
    assert(vptr!=NULL);
    vertexInit(vptr);
    return vptr;
}  /* End of vertexNew*/



/*
 * vertexCopy
 *
 *      Do a hard copy of vertex. 
 */

void 
vertexCopy (Vertex *vdest, Vertex *vsrc)
{
    pointCopy(vdest->p, vsrc->p);
    pointCopy(vdest->normal, vsrc->normal);
    vdest->facets = vsrc->facets;
    vdest->elist  = vsrc->elist;
    vdest->owner = vsrc->owner;
}  /* End of vertexCopy*/



/*
 * expandArray
 *
 *      Expands a generic array.
 */

int 
expandArray (void **ary, int elementSize, int* currentSize, int expandSize)
{
    int *newAry;

    newAry = (void *) realloc((void *) *ary,
			     (*currentSize + expandSize) * elementSize);
    if (newAry != NULL){
	*ary = newAry;
	*currentSize = *currentSize + expandSize;
	return 0;
    }
    else {
	fprintf(stderr, "Can't reallocate mem, in expandArray\n");
	return 1;
    }
}  /* End of expandArray*/






/*
 * findVertex
 *
 *     Finds and returns a vertex from object array. 
 */

int
findVertex(ModelObject *obj, Point pt)
{
  int i;

  for (i = 0; i <= obj->vertCnt; i++){
    zdo6 printf(" comparing %f %f %f with %f %f %f \n",
	       pt[0], pt[1],pt[2],
	       obj->varray[i].p[0],
	       obj->varray[i].p[1],
	       obj->varray[i].p[2]);
    if (pointEqual(obj->varray[i].p, pt)){
      zdo6 printf("Match!\n");
      return i;
    }
  }
  return -1;
}  /* End of findVertex */



/*
 * varrayAddNewVertex
 *
 *      Adds a new vertex to a vertex array, expand array if necessary.
 */

Vertex *
varrayAddNewVertex (Vertex **varrayPtr, int *varraySize,
		    int *vertCntPtr, Point newPt)
{
    int currentSize;    /* Current size of varray */
    Vertex *varray;
    Vertex *newvarray;

    
    varray = *varrayPtr;
    newvarray = varray;
    currentSize = *varraySize;
    *vertCntPtr = *vertCntPtr + 1;
    if (*vertCntPtr >= currentSize)
	{
	    /* Expand array to twice its size */
	  int err;

	    zdo6 printf("\n array max size exceeded, expanding to %d", 
		       currentSize * 2); 
	   
	    /* WARNING! Do not get rid of newvarray or varray because strange
	     * memory fault will happen. Not yet understood why. --mchow 11/23/96
	     */
	    
	    err = expandArray((void **) varrayPtr, sizeof(Vertex), varraySize, currentSize);
	    newvarray = *varrayPtr;
	    /* If there is more memory, of course */
	    if (!err){
		zdo6 printf("\n New varray is valid!");
		*varraySize = currentSize * 2;
		*varrayPtr = newvarray;
		varray = newvarray;
	    }
	    else {
		printf( "can't allocate new Vertex! no more memory!\n");
		*vertCntPtr--;
	    }
	}

    zdo6 printf("\n point copying..");
    pointCopy(varray[*vertCntPtr].p, newPt);
    vertexInit(&varray[*vertCntPtr]);
    return newvarray;
}  /* End of varrayAddNewVertex*/



/*
 * createVert
 *
 *      Look up the index of a point in a vertex array, 
 *      create a new vertex if necessary.
 */
	
int 
createVert(Vertex **varrayPtr, int *varraySize, int *vertCntPtr, 
	       Point newPt)
{
    int i;
    Vertex *varray;

    zdo6 fprintf(stderr, "before adding the vertCnt is:%d\n", *vertCntPtr);
    *varrayPtr = varrayAddNewVertex(varrayPtr, varraySize, vertCntPtr, newPt);
    zdo6 fprintf(stderr, "new index is:%d\n", *vertCntPtr); 
    
    return *vertCntPtr; /* Should be one more than before */
}  /* End of createVert*/




/*
 * findCreateVert
 *
 *      Look up the index of a point in a vertex array, 
 *      create a new vertex if the index isn't found. This is 
 *      used to insert new vertices when we don't know if it exists
 *      already. (Used by cube tesselator).
 */
	
int 
findCreateVert(Vertex **varrayPtr, int *varraySize, int *vertCntPtr, 
	       Point newPt, float equalThreshold)
{
    int i;
    Vertex *varray;
    
    zdo6 fprintf(stderr, "looking up vert,\n");
    zdo6 printPoint( newPt);
    varray = *varrayPtr;
    for (i = *vertCntPtr ; i >= 0; i--){
      zdo6 fprintf(stderr, "looking at %d,\n", i);
      zdo6 printPoint( varray[i].p);
    
	/* Found! Return index, i */
	if ((fabs(varray[i].p[0] - newPt[0]) < equalThreshold) &&
	    (fabs(varray[i].p[1] - newPt[1]) < equalThreshold) &&
	    (fabs(varray[i].p[2] - newPt[2]) < equalThreshold)){
	  zdo6 fprintf(stderr, "found index %d\n", i);
	    return i;
	}

    }
    zdo6 fprintf(stderr, "not found, creating new vert\n");
    createVert(varrayPtr, varraySize, vertCntPtr, newPt);
    return *vertCntPtr; /* Should be one more than before */
}  /* End of findCreateVert*/



int 
findCreateVertFast(Vertex **varrayPtr, int *varraySize, int *vertCntPtr, 
		   Point newPt, int timeOut, float equalThreshold)
{
    int i;
    Vertex *varray;
    int cnt = 0;

    zdo6 fprintf(stderr, "looking up vert,\n");
    zdo6 printPoint( newPt);
    varray = *varrayPtr;
    for (i = *vertCntPtr ; ((i >= 0) && (cnt < timeOut)); i--){
      zdo6 fprintf(stderr, "looking at %d,\n", cnt);
      zdo6 printPoint( varray[i].p);
      cnt++;
	/* Found! Return index, i */
	if ((fabs(varray[i].p[0] - newPt[0]) < equalThreshold) &&
	    (fabs(varray[i].p[1] - newPt[1]) < equalThreshold) &&
	    (fabs(varray[i].p[2] - newPt[2]) < equalThreshold)){
	  zdo6 fprintf(stderr, "found index %d\n", i);
	    return i;
	}

    }
    zdo6 fprintf(stderr, "not found, creating new vert\n");
    createVert(varrayPtr, varraySize, vertCntPtr, newPt);
    return *vertCntPtr; /* Should be one more than before */
}  /* End of findCreateVert*/


/* Edge Methods */

/*
 * edgeNew
 *
 *      Return a newly allocated edge.
 */

Edge *
edgeNew(Part *owner, Vertex *v1, Vertex *v2)
{
    Edge *eptr;

    eptr = (Edge *) malloc(sizeof(Edge));
    assert(eptr!=NULL);
    eptr->v1 = v1;
    eptr->v2 = v2;
    eptr->hard = 0;
    eptr->next = owner->edges;
    owner->edges = eptr;
    return(eptr);
}  /* End of edgeNew*/



static int edgeCnt = 0;


/*
 * edgeDestroy 
 *
 *      Frees up an edge.
 */

void
edgeDestroy(Edge *eptr)
{
  edgeCnt++;
  free(eptr);
}


/*
 * applyToEdges (not used for now).
 *
 *      Apply a function, f, over each edge of the edge list.
 *      f must be a function that takes an edge ptr and returns
 *      a non-zero value if it wishes to continue applying.
 */

#if 0
int
applyToEdges (Edge *elist, int (*f)(Edge *))
{
    Edge *eptr;
    
    for (eptr = elist; eptr != NULL; eptr = eptr->next){
	if (!f(eptr)) 
	    return(0); 
    }

    return (1);
} 
#endif 




/* Facet Routines */


/*
 * facetNew
 *
 *      Make an initialized facet.
 */

static float DefaultMarkerColor[4] = {1.0, 0.0, 1.0, 1.0};

Facet *
facetNew(Part *owner)
{
    Facet *fptr;
    
    fptr = (Facet *) malloc(sizeof(Facet));
    fptr->vindex = (int *) malloc(sizeof(int) * MIN_VERTS_PER_FACET);
    fptr->vindexSize = MIN_VERTS_PER_FACET;
    fptr->numVerts = 0;

    fptr->tindex = (int *) malloc(sizeof(int) * MIN_VERTS_PER_FACET);
    fptr->tindexSize = MIN_VERTS_PER_FACET;
    fptr->numTexCoords = 0;
    
    fptr->edges = NULL;
    fptr->owner = owner;
    fptr->timeStamp = 0;
    fptr->selected = 0;
    fptr->pickedColor = rgbaCreate(DefaultMarkerColor);
    fptr->adjacencyCnt = 0;
    fptr->radius = -1.0;
    fptr->region = NULL;
    fptr->boundaryFacet = 0;
    fptr->normal[0] = 0.0;
    fptr->normal[1] = 0.0;
    fptr->normal[2] = 1.0;
    fptr->hasHardEdge = 0;
    fptr->bbox = NULL;
    return(fptr);
}  /* End of facetNew*/



/*
 * facetDestroy
 *
 *      Free facet.
 */

static int fptrCnt = 0;
void
facetDestroy(Facet *fptr)
{
  Edge *eptr;
  int i;

  free(fptr->vindex);
  /* edges are owned and destroyed by the part */
  free(fptr->edges);
  free(fptr->pickedColor);
  free(fptr->tindex);
  free(fptr);
  fptrCnt++;
}  /* End of facetDestroy*/




/*
 * facetAddVindex
 *
 *      Add new vertex index to facet.
 */

void
facetAddVindex(Facet *fptr, ModelObject *obj, VertIndex index)
{
/* Looks just like extendStrip in triStrips.c */

    int *newArray;
    int currentSize;
    
    currentSize = fptr->vindexSize;
    if (fptr->numVerts >= currentSize)
	{
	    
/* Expand array to twice its size */
	    int err;

	    zdo6 fprintf(stderr, "array max size exceeded, expanding to %d\n", 
		       currentSize * 2); 
	    err =  expandArray ((void **) (& fptr->vindex), sizeof(VertIndex),
				       &fptr->vindexSize, currentSize);
	    if (err) {
		fprintf(stderr, "can't allocate new Vertex! no more memory!\n");
		fptr->numVerts--;
	    }
	}

    /* Update facet list info for this vertex */
    flistAddFacet(&(obj->varray[index].facets), fptr);
    fptr->vindex[fptr->numVerts++] = index;

    zdo6 fprintf(stderr, "put %d\n", index);
}  /* End of facetAddVindex*/




/*
 * facetEdgesInit
 *
 *      Initialize edges.
 */

void 
facetNewEdges(Facet *fptr)
{
  int i;

  fptr->edges = (Edge **) malloc(sizeof(Edge *) * fptr->numVerts);
  assert(fptr->edges != NULL);
  for (i = 0; i < fptr->numVerts; i++)
    fptr->edges[i] = NULL;
}




/*
 * facetAddEdge
 *
 *      Add an edge to a facet edge list.
 *      Handles empty lists as well.
 */

void
facetAddEdge(Facet *fptr, int i, Edge *eptr)
{
    fptr->edges[i] = eptr;
}  /* End of facetAddEdge*/



/*
 * facetGetEdge
 *
 *      Given two consecutive positions in the vindex array of
 *  a facet, find the edge.
 */

Edge *
facetGetEdge(Facet *fptr, int p, int q)
{
  Edge *eptr;
  VertIndex vid1, vid2;

  if ((p > fptr->numVerts - 1) || 
      (q > fptr->numVerts - 1))
    return NULL;

  eptr = fptr->edges[p];
  vid1 = eptr->vid1;
  vid2 = eptr->vid2;
  if (((fptr->vindex[p] == vid1) && (fptr->vindex[q] == vid2)) ||
      ((fptr->vindex[p] == vid2) && (fptr->vindex[q] == vid1)))
    return eptr;

  eptr = fptr->edges[q];
  vid1 = eptr->vid1;
  vid2 = eptr->vid2;
  if (((fptr->vindex[p] == vid1) && (fptr->vindex[q] == vid2)) ||
      ((fptr->vindex[p] == vid2) && (fptr->vindex[q] == vid1)))
    return fptr->edges[q];
  return NULL;
}  /* End of facetGetEdge*/



void
facetFindBbox(Facet *fptr)
{
  BBox *fptrBbox = bboxCreate();
  int i;

  for (i = 0; i < fptr->numVerts; i++){
    Vertex *vert;

    vert = &fptr->owner->obj->varray[fptr->vindex[i]];
    bboxExtendPoint(fptrBbox, vert->p);
    fptr->bbox = fptrBbox;
  }
}



void
findFacetAdjacency(Part *part)
{
  int i;
  Facet *fptr;
  Edge *edge;

  for(fptr = part->facets; fptr != NULL; fptr = fptr->next){
    for (i = 0; i < fptr->numVerts; i++){
      edge = fptr->edges[i];
      if (edge->hard == 0)
	fptr->adjacencyCnt++;
    }
    if (fptr->adjacencyCnt == 2){
      flistAddFacet(&(part->cornerFacets), fptr);
      zdo6 printf("Adding a corner to %s \n", part->name);
    }
  }
}

void
facetPrint(Facet *fptr)
{
  int i;

  printf("f %x ", fptr);
  for (i = 0;i < fptr->numVerts; i++)
    printf(" %d", fptr->vindex[i]);
  printf("\n");
}


/*
 * applyToFacets
 *
 *      Apply a function, f, over each facet of the facet list.
 *      f must be a function that takes an edge ptr and returns
 *      a non-zero value if it wishes to continue applying.
 *
 */


int
applyToFacets (Facet *flist, int (*f)(Facet *, int count, void *data),
	       void *data)
{
    Facet *fptr;
    int count = 0;

    for (fptr = flist; fptr != NULL; fptr = fptr->next, count++){
	if (!f(fptr, count, data)) 
	    return(0); /* A False means the function wants out. */
    }
    /* exited normally */
    return (1);
}

/*
 * facetForEachAdjFacet
 *
 *      Apply a function, f, over each adj facet of the facet.
 *      f must be a function that takes an fptr and returns
 *      a non-zero value if it wishes to continue applying.
 *
 */


int
facetForEachAdjFacet (Facet *currFptr, int (*f)(Facet *, Edge *, void *data),
		      void *data)
{
  int i;

  /* For each edge of this facet, get adjacent facet and test it
   * for the starting facet that maximize mesh buffer usage. */

  for (i = 0; i < currFptr->numVerts; i++){
    Edge *edge;
    Facet *look1Fptr;

    edge = currFptr->edges[i];

    /* Get adjacent facet */
    if (edge->f1 == currFptr)
      look1Fptr = edge->f2;
    else
      look1Fptr = edge->f1;

    zdo6 printf("L1. Checking out nbr fptr %x .\n", 
	       look1Fptr);
    
    /* If haven't seen this facet before, */
    if (look1Fptr != NULL){
      if (!f(look1Fptr, edge, (void *) data))
	break;
    }
  }
  return 1;
}  /* End of facetForEachAdjFacet*/



/* FacetList Routines */

/*
 * flistNewNode
 *
 *      Adds a new node to facet list.
 */

FacetList *
flistNewNode ()
{
    FacetList *flist;
    
    flist = (FacetList *) malloc(sizeof(FacetList));
    flist->current = NULL;
    flist->next = NULL;
    return flist;
}  /* End of flistNewNode*/



static int flistCnt = 0;


/*
 * flistDestroy
 *
 *      Frees up a flist.
 */

void
flistDestroy(FacetList *flist)
{
  FacetList *flptr, *next;
  
  for(flptr = flist; flptr != NULL; flptr = next){
    next = flptr->next;
    flistCnt++;
    free(flptr);
  }
}




/*
 * flistAddFacet
 *
 *      Add a new facet to a facetList (for a vertex).
 */

void
flistAddFacet (FacetList **flistptr, Facet *fptr)
{
    
    FacetList *newNode;   /* New facetList node */
    FacetList *flist;     /* Temp FacetList ptr */

    flist = *flistptr;

    /* Empty list, make fptr the first. */
    if (flist->current == NULL)
	{
	    flist->current = fptr;
	    return;
	}
    /* Normal case, add Facet as head */
    
    newNode = flistNewNode();
    newNode->current = fptr;
    newNode->next = flist;
    *flistptr = newNode;
}  /* End of flistAddFacet*/

/* EdgeList */


/*
 * edgeListAddEdge
 *
 *     Adds an edge to an edge list made from a linked list (checks 
 * for redundancy.
 */

void
edgeListAddEdge(LinkedList **elist, Edge *eptr)
{
  LinkedList *elptr;

  linkedListAddNode(elist, (void *) eptr);
}  /* End of edgeListAddEdge  */



/* TriStrip Methods */


/*
 * triStripNew
 *
 *      Make a new strip.
 */

TriStrip *
triStripNew()
{
    TriStrip *ts;

    ts = (TriStrip *) malloc(sizeof(TriStrip));
    ts->length = 0;
    ts->next = NULL;
    ts->tri = NULL;
    ts->triCnt = 0;
    return ts;
}  /* End of triStripNew */



/*
 * triStripCreate
 *
 *      Make a new strip AND initialize triangle array.
 */

TriStrip *
triStripCreate()
{
    TriStrip *ts;

    ts = (TriStrip *) malloc(sizeof(TriStrip));
    ts->tri = (VertIndex *) malloc(sizeof(VertIndex) * AVE_STRIP_SIZE);
    if (ts->tri == NULL){
      printf("Warning! triStripCreate can't malloc tri array!\n");
      return NULL;
    }
    ts->arraySize = AVE_STRIP_SIZE;
    ts->length = 0;
    ts->next = NULL;
    ts->triCnt = 0;
    return ts;
}  /* End of triStripCreate*/




/*
 * triStripExtendStrip
 *
 *      Adds a new vertex index to a strip.
 */

void 
triStripExtendStrip (TriStrip *strip, int index)
{

    int *newArray;
    int currentSize;

    currentSize = strip->arraySize;
    if (strip->length >= currentSize)
	{
	    
/* Expand array to twice its size */
	    int err;

	    zdo6 fprintf(stderr, "array max size exceeded, expanding to %d\n", 
		       strip->arraySize * 2); 
	    err = expandArray((void **) (& strip->tri), sizeof(int),
				       &strip->arraySize, currentSize);
	    if (err) {
		fprintf(stderr, "can't allocate new Vertex! no more memory!\n");
		strip->length--;
	    }
	}

    strip->tri[strip->length++] = index;
    strip->triCnt++;
    zdo6 fprintf(stderr, "put %d\n", index);
}  /* End of extendStrip*/




/*
 * triStripPrint
 *
 *      Pretty printer for triStrip
 */

void
triStripPrint (TriStrip *strip, Vertex *varray)
{
    int i;
    Vertex *v;

    fprintf(stderr, "strip:\n");
    for (i = 0; i < strip->length; i++){
	v = &varray[strip->tri[i]];
	printPoint(v->p);
	fprintf(stderr, " %d n %f %f %f", strip->tri[i], v->normal[0], 
	       v->normal[1], v->normal[2]);
    }
}  /* End of triStripPrint*/



    
/* TriStrips Methods */

void
triStripsInit (TriStrips *tstrips)
{
    tstrips->strips = NULL;
    tstrips->numStrips = 0;
    tstrips->triCnt = 0;
}




/*
 * triStripNew
 *
 *      Creates a new triStrips.
 */

TriStrips *
triStripsNew ()
{
    TriStrips *strips;

    strips = (TriStrips *) malloc(sizeof(TriStrips));
    triStripsInit(strips);
    return strips;
} 




/*
 * triStripsDestroy
 *
 *      Free up triStrips by freeing up triangle index array 
 * and the individual strips.
 * 
 */

static triStripCnt = 0;

void 
triStripsDestroy(TriStrips *tstrips)
{
  
  TriStrip *ts, *next;

  for (ts = tstrips->strips; ts != NULL; ts = next){
    next = ts->next;
    free(ts->tri);
    free(ts);
    triStripCnt++;
  }
}
 



/*
 * triStripsAddStrip
 *
 *    Adds a new strip to the strips.  
 */
   
void 
triStripsAddStrip (TriStrips *tstrips, TriStrip *ts)
{
  TriStrip *s;

    if (tstrips->strips == NULL) {
	tstrips->strips = ts;
	tstrips->numStrips++;
	return;
    }
    zdo6 for (s = tstrips->strips; s->next != NULL; s = s->next);
    zdo6 s->next = ts;
    ts->next = tstrips->strips;
    tstrips->strips = ts;
    tstrips->numStrips++;
    return;
}  /* End of triStripsAddStrip*/




/* Surface Methods */

/* Default colors */ 
static float defaultDiffuse[4] = {0.25, 0.5, 0.75, 1.0};
static float defaultSpecular[4] = {0.9, 0.9, 0.9, 1.0};
static float defaultShininess[1] = {50.0};
static float defaultEmission[4] = {0.0, 0.0, 0.0, 1.0};
static float defaultAmbient[4] = {0.2, 0.2, 0.2, 1.0};



/*
 * rgbaCreate
 *
 *      Create and makes a new copy of a rgba color.      
 */

float *
rgbaCreate(float c[])
{
    float *newColor;
    int i;

    newColor = (float *) malloc(sizeof(float) * 4);
    for (i = 0; i < 4; i++)
	newColor[i] = c[i];
    return newColor;
}  /* End of rgbaCreate*/





/*
 * rgbaCopy
 *
 *      Copies rgba.
 */

float *
rgbaCopy(float dest[], float src[])
{
  int i;
    for (i = 0; i < 4; i++)
	dest[i] = src[i];
    return dest;
}  /* End of rgbaCopy*/




/*
 * rgbaPrint
 *
 *      Output rgba to file.
 */

void
rgbaPrint(FILE *fp, int indent, float c[])
{
  int i;

  spaces(fp, indent);
  for (i = 0; i < 4; i++)
    fprintf(fp, "%f ", c[i]);
  fprintf(fp, "\n");
}




/*
 * surfCreate
 * 
 *      return a new, uninitialized material Surface          
 * 
 */

SurfaceProp *
surfCreate()
{
    return ((SurfaceProp *) malloc(sizeof(SurfaceProp)));
}  /* End of createSurf */



/*
 * surfCreateEmpty
 * 
 *     Creates a empty surface.
 */

SurfaceProp *
surfCreateEmpty()
{
    SurfaceProp *surf;

    surf = surfCreate();
    surf->diffuse = NULL;
    surf->specular = NULL;
    surf->shininess = NULL;
    surf->emission = NULL;
    surf->ambient = NULL;
    return surf;
}  /* End of createSurf */


/*
 * surfCreateDefault
 * 
 *     Creates a default surface.
 */

SurfaceProp *
surfCreateDefault()
{
    SurfaceProp *surf;

    surf = surfCreate();
    surf->diffuse = rgbaCreate(defaultDiffuse);
    surf->specular = rgbaCreate(defaultSpecular);
    surf->shininess = (float *) malloc(sizeof(float));
    surf->shininess[0] = defaultShininess[0];
    surf->emission = rgbaCreate(defaultEmission);
    surf->ambient = rgbaCreate(defaultAmbient);
    return surf;
}  /* End of createSurf */



/*
 * surfDestroy
 *
 *      Free up surface.
 */

void
surfDestroy(SurfaceProp *surf)
{
  if (surf->diffuse) free(surf->diffuse);
  if (surf->specular) free(surf->specular);
  if (surf->shininess) free(surf->shininess);
  if (surf->emission) free(surf->emission);
  if (surf->ambient) free(surf->ambient);
  free(surf);
}  /* End of surfDestroy */




/*
 * surfCopy
 *
 *      Copies a surface.
 */

void
surfCopy(SurfaceProp *dest, SurfaceProp *src)
{

    dest->diffuse = rgbaCreate(src->diffuse);
    dest->specular = rgbaCreate(src->specular);
    dest->shininess[0] = src->shininess[0];
    dest->emission = rgbaCreate(src->emission);
    dest->ambient = rgbaCreate(src->ambient);
}  /* End of surfCopy */



/*
 * surfSetAllColorMasks
 *
 *      Sets all color masks one at a time.
 */

void
surfSetAllColorMasks(Attrib *attrib)
{
  ATTRIB_SET_MASK(attrib, ATTRIB_DIFFUSE_COLOR);
  ATTRIB_SET_MASK(attrib, ATTRIB_SPECULAR_COLOR);
  ATTRIB_SET_MASK(attrib, ATTRIB_AMBIENT_COLOR);
  ATTRIB_SET_MASK(attrib, ATTRIB_EMISSIVE_COLOR);
  ATTRIB_SET_MASK(attrib, ATTRIB_EXPONENT);
}  /* End of surfSetAllColorMasks*/


/*
 * surfPrint
 *
 *      Output surface to a file.
 */

void
surfPrint(FILE *fp, int indent, SurfaceProp *surf)
{
  int dent = 8;
  spaces(fp, indent);
  fprintf(fp, "Surface {\n");

  spaces(fp, indent + dent);
  fprintf(fp, "ambient");
  rgbaPrint(fp, 1, surf->ambient);

  spaces(fp, indent + dent);
  fprintf(fp, "diffuse");
  rgbaPrint(fp, 1, surf->diffuse);

  spaces(fp, indent + dent);
  fprintf(fp, "specular");
  rgbaPrint(fp, 1, surf->specular);
   
  spaces(fp, indent + dent);
  fprintf(fp, "shininess %f\n", surf->shininess[0]);

  spaces(fp, indent);
  fprintf(fp, "}\n");
}  /* End of surfPrint*/





/* Part Routines */

/*
 * partCreate
 *
 *      Create a newly initialized part.
 */

Part *
partCreate (ModelObject *ownerObj)
{
    Part *part;
    Attrib *attrib;

    part = (Part *) malloc(sizeof(Part));
    part->obj = ownerObj;
    part->facets = NULL;
    part->name = (char *) malloc(sizeof(char) * 100);
    part->triStrips = triStripsNew();
    part->lineStrips = (LineStrips *) malloc(sizeof(LineStrips));
    part->edges = NULL;
    part->triCnt = 0;
    part->edgeCnt = 0;
    part->pointCnt = 0;
    part->meshInfo = NULL;

    attrib = attribNew();
    attrib->frontSurf = surfCreateDefault();
    ATTRIB_CLEAR_MASK(attrib, ATTRIB_ALL);
    part->attrib = attrib;
    part->bbox = bboxCreate();
    part->cornerFacets = flistNewNode();
    part->regions = linkedListNewNode();
    part->aveStripLen = 0;
    part->hardEdgeCount = 0;
    part->name = NULL;
    return part;
}  /* End of partsCreate */




/*
 * partFindBbox
 *
 *      Find the bounding box for a part by going through all of 
 * extending its box for all of its points.
 */

void
partFindBbox(Part *p)
{
  TriStrip *s;
  Vertex *vert;
  int i;

  for (s = p->triStrips->strips; s != NULL; s = s->next) {
    for (i = 0; i < s->length; i++) {
      /* Get vertex position and normal */
      if ((s->tri[i] < 0) ||
	  (s->tri[i] > p->obj->vertCnt))
	continue;
      vert = &p->obj->varray[s->tri[i]];
      bboxExtendPoint(p->bbox, vert->p);
    } /* End of for each vertex in the strip */
  } /* End of for each triangle strip */
}  /* End of partFindBbox */




/*
 * partDestroy
 *
 *      Free up all allocated space for a part.
 */

void
partDestroy(Part *part)
{
  Facet *fptr, *fptrNext;
  Edge *eptr, *eptrNext;
  LinkedList *lptr, *lptrNext;
  free(part->name);


  for (fptr = part->facets; fptr != NULL; fptr = fptrNext){
    fptrNext = fptr->next;
    facetDestroy(fptr);
  }

  for (eptr = part->edges; eptr != NULL; eptr = eptrNext){
    eptrNext = eptr->next;
    edgeDestroy(eptr);
  }

  for (lptr = part->regions; lptr != NULL; lptr = lptrNext){
    lptrNext = lptr->next;
    meshRegionDestroy((MeshRegion *)lptr->current);
  }
  flistDestroy(part->cornerFacets);

  triStripsDestroy(part->triStrips);
  meshInfoDestroy(part->meshInfo);
  free(part->lineStrips);
  if (part->name != NULL) {
      free(part->name);
  }
  attribDestroy(part->attrib);
  free(part);
    
}  /* End of partDestroy*/




/*
 * partAddFacet
 *
 *      Add an Facet to a part Facet list.
 *      Handles empty lists as well.
 */

void
partAddFacet(Part *part, Facet *fptr)
{

    /* Empty list, make fptr the first. */
    if (part->facets == NULL)
	{
	    part->facets = fptr;
	    fptr->next = NULL;
	    fptr->prev = NULL;
	    return;
	}
    /* Normal case, add Facet as head */
    part->facets->prev = fptr;
    fptr->next = part->facets;
    part->facets = fptr;
}  /* End of partAddFacet*/





/* BBox methods*/

/*
 * bboxCreate
 *
 *      Creates a newly initialized bounding box.
 */

BBox * 
bboxCreate()
{
    BBox *bbox;
    int i;

    bbox = (BBox *) malloc(sizeof(BBox));
    for(i = 0; i < 3; i++){
	bbox->min[i] = MAXFLOAT;
	bbox->max[i] = -MAXFLOAT;
    }
    
    return bbox;
}  /* End of bboxCreate*/





/*
 * bboxDestroy
 *
 *      Destroys a bbox.
 */

void
bboxDestroy(BBox *bbox)
{
  free(bbox);
}




/*
 * bboxWrite
 *
 *      Outputs a bbox to file.
 */

void
bboxWrite(FILE *fp, int indent, BBox *bbox)
{
  if (bbox->min[0] == HUGE)
    return;
  spaces(fp, indent);
  fprintf(fp, "Bbox");
  pointPrint(fp, 1, bbox->min);
  spaces(fp, indent+4);
  pointPrint(fp, 1, bbox->max);
}  /* End of bboxWrite*/





/*
 * bboxCopy
 *
 *      Copys a newly initialized bounding box.
 */

BBox * 
bboxCopy(BBox *dest, BBox *src)
{
    int i;
    for(i = 0; i < 3; i++){
	dest->min[i] = src->min[i];
	dest->max[i] = src->max[i];
    }
 return dest;
}  /* End of bboxCopy*/




/*
 * bboxPrint
 *
 *      Prints bbox sizes.
 */

void
bboxPrint(BBox *box)
{
  int i;

  for(i = 0; i < 3; i++)
    fprintf(stderr, " %d min %f max %f\n", i, box->min[i], box->max[i]);
}  /* End of bboxPrint */
     


int  ptInBbox(Point p, BBox *bbox)
{
  return (((p[0] >= bbox->min[0]) && (p[0] <= bbox->max[0])) &&
	  ((p[1] >= bbox->min[1]) && (p[1] <= bbox->max[1])) &&
	  ((p[2] >= bbox->min[2]) && (p[2] <= bbox->max[2])));
}

/*
 * bboxMerge
 *
 *      Merges a newly initialized bounding box.
 */

BBox * 
bboxMerge(BBox *dest, BBox *src, ModelTrans *transform, Point scale)
{
    int i;

#if 0
    zdo fprintf(stderr, "merging box:\n");
    zdo bboxPrint(src);
    zdo fprintf(stderr, "with:\n");
    zdo bboxPrint(dest);
#endif
#if 0 
    /* Used to be..*/
	dest->min[i] = minf(dest->min[i], 
			    src->min[i] * transform->scale[i]
			    + (transform->translate[i] - 
#endif			       transform->center[i]));
	  /* Forget about translation, just merge the bounding boxes! */
    for(i = 0; i < 3; i++){
	dest->min[i] = minf(dest->min[i], 
			    src->min[i] * transform->scale[i]);
	dest->max[i] = maxf(dest->max[i], 
			    src->max[i] * transform->scale[i]);
    }
#if 0
    zdo fprintf(stderr, "final\n");
    bboxPrint(dest);
#endif
    return dest;
}  /* End of bboxMerge*/





/*
 * bboxExtendPoint
 *
 *      Given a point, update the bounding box.
 */

void
bboxExtendPoint (BBox *bbox, Point p)
{
    int i;
    
    for(i = 0; i < 3; i++){
	if (bbox->min[i] > p[i])
	    bbox->min[i] = p[i];
	if (bbox->max[i] < p[i])
	    bbox->max[i] = p[i];
    }
}  /* End of bboxExtendPoint*/




/*
 * bboxGetVerts
 *
 *      
 */

void
bboxGetVerts(BBox *bbox, Point verts[])
{
    int i;
}



/* ModelTrans Methods */

/*
 * transformCreate
 *
 *      Create and initialize a new transform struct.
 */

ModelTrans *
transformCreate()
{
  ModelTrans *ms;
  int i;

  ms = (ModelTrans *) malloc(sizeof(ModelTrans));
  for (i = 0; i < 16 ; i++)
    ms->rotMat[i] = identityMat[i];
  for (i = 0; i < 3 ; i++) {
    ms->scale[i] = 1.0;
    ms->center[i] = 0.0;
    ms->translate[i] = 0.0;
  }
  ms->rotDeg = 0.0;
  ms->xDeg = 0.0;
  /* Set to 1.0 by default */
  ms->yDeg = 1.0;
  ms->rotAnimDeg = 0.0;
  ms->xAnimDeg = 0.0;
  ms->yAnimDeg = 1.0;

  return ms;
}  /* End of transformCreate*/



/*
 * transformDestroy
 *
 *      Destroys a transform structure.
 */

void
transformDestroy(ModelTrans *ms)
{
  free(ms);
}



/*
 * transformReset
 *
 *      Reset the rotation matrix, and current rotations of transform.
 * Preserves scaling and translation.
 */

ModelTrans *
transformReset(ModelTrans *mx, int class)
{
  ModelTrans *newmx;
  int i;
    
  newmx = transformCreate();
#if 0
  for (i = 0; i < 3 ; i++) {
    newmx->scale[i] = mx->scale[i];
    newmx->center[i] = mx->center[i];
    if (class == NODE_LEAF)
      newmx->translate[i] = mx->center[i];
  }
#endif
  transformDestroy(mx);
  return newmx;
}  /* End of transformReset*/




/*
 * transformCopy
 *
 *      Copy a transform struct.
 */

void
transformCopy(ModelTrans *dest, ModelTrans *src)
{
  ModelTrans *ms;
  int i;

  ms = dest;
  for (i = 0; i < 16 ; i++)
    ms->rotMat[i] = src->rotMat[i];
  for (i = 0; i < 3 ; i++) {
    ms->scale[i] = src->scale[i];
    ms->translate[i] = src->translate[i];
    ms->center[i] = src->center[i];
  }
  ms->xAnimDeg = src->xAnimDeg;
  ms->yAnimDeg = src->yAnimDeg;
  ms->rotAnimDeg = src->rotAnimDeg;
  ms->xDeg = src->xDeg;
  ms->yDeg = src->yDeg;
  ms->rotDeg = src->rotDeg;

} 




/*
 * transformPrint
 *
 *      Writes a transform to a file.
 */

void
transformPrint(FILE *fp, int start, ModelTrans *trans)
{
  
  int indent = 2;
  int i;

  spaces(fp, start);
  fprintf(fp, "Transform {\n");

  spaces(fp, start + indent);
  fprintf(fp, "translate");
  pointPrint(fp, 1, trans->translate);

  spaces(fp, start + indent);
  fprintf(fp, "scale");
  pointPrint(fp, 1, trans->scale);
  
  spaces(fp, start + indent);
  fprintf(fp, "center");
  pointPrint(fp, 1, trans->translate);

  spaces(fp, start + indent);
  fprintf(fp, "rotate %f %f %f", trans->xAnimDeg, trans->yAnimDeg,
	  trans->rotAnimDeg);

  spaces(fp, start + indent);
  fprintf(fp, "rotMat ");
  for(i = 0; i < 16; i++){
    if ((i%4) == 0){
      fprintf(fp, "\n");
      spaces(fp, start + indent);
    }
    fprintf(fp, " %f", trans->rotMat[i]);
  }
  fprintf(fp, "\n");
  spaces(fp, start);
  fprintf(fp, "}\n");
}  /* End of transformPrint*/





/* ModelObject Methods */

/*
 * objAddPart
 *
 *      Adds a new part to an object. 
 */

void
objAddPart(ModelObject *obj, Part *newPart)
{

/* Looks just like extendStrip in triStrips.c */
    int currentSize;
    int index;

    currentSize = obj->maxPartCnt;
    if (obj->partCnt >= currentSize)
	{
	    Part **newAry;

/* Expand array to twice its size */
	    /* Had to do this manually */

	    newAry = (Part **) malloc((currentSize * 2) * sizeof(Part *));
	    
	    if (newAry != NULL){
		int i;

		for (i = 0; i < obj->partCnt; i++)
		    newAry[i] = obj->parts[i];
		obj->parts = newAry;
		obj->maxPartCnt = currentSize * 2;
	    }
	    else {
		fprintf(stderr, "Can't reallocate mem, in objAddPart\n");
		obj->partCnt--;
	    }
	}

    
    obj->parts[obj->partCnt++] = newPart;
    zdo6 fprintf(stderr, "put %d\n", index);    
 
}  /* End of objAddPart*/





/* 
 * objCreate
 *
 *      Creates a new obj object.     
 */

ModelObject *
objCreate()
{
    
    ModelObject *obj;
    ModelTrans *ms;
    Part   *part;
    int i, j, index;
    Facet *fptr; /*Temp facet ptr */
    LineStrips *ls;
    TriStrip  *triStrip;         /*Temp triStrip ptr*/
    TriStrips *triStrips;
    Vertex    *varray;
    
    /* Allocate parts of a new obj */
    obj = (ModelObject *) malloc(sizeof(ModelObject));
    obj->filepath = NULL;
    obj->parts = (Part **) malloc(sizeof(Part *) * MIN_OBJ_PART_CNT);
    obj->maxPartCnt = MIN_OBJ_PART_CNT;
    obj->partCnt = 0;
    obj->varraySize = MIN_VERTS_PER_OBJ;
    /* Make an array of vertices */
    varray = (Vertex *) malloc(sizeof(Vertex) * obj->varraySize); 

    memset(varray, 0, sizeof(Vertex) * obj->varraySize);

    obj->varray = varray;
    obj->vertCnt = -1;


    obj->bbox = bboxCreate();
    obj->attrib = attribCreate();
    obj->transform = transformCreate();
    obj->transform->translate[2] = -5.0;
    obj->nodeCnt = 0;
    obj->dlistInitialized = 0;
    return(obj);
} /* End of objCreate */	





/*
 * objIsInternal
 *
 *     Is object an internal primitive or prototype object?
 */

int
objIsInternal(ModelObject *obj)
{
  if ((strcmp(obj->name, "cube") == 0) ||
      (strcmp(obj->name, "sphere") == 0) ||
      (strcmp(obj->name, "SplinePointIcon") == 0) ||
      (strcmp(obj->name, "LightIcon") == 0))
    return 1;
  else return 0;
}  /* End of objIsInternal*/





/*
 * objIsPrimTess
 *
 *      Is object a primitive with different tesselation?
 */

int
objIsPrimTess(ModelObject *obj)
{
  char *str;

  /* Currently, cube destructor isn't quite working yet. Will be fixed.*/
  if (strlen(obj->name) > 4) {
    str = strCut(obj->name, 0, 4);
    if (strcmp(str, "cube") == 0){
      free(str);
      return 1;
    }
  }
  return 0;
}  /* End of objIsPrimTess*/





/*
 * objDestroy
 *
 *     This is the top-level routine that free up memory. 
 *     Like in C++ or other object oriented languages, we call 
 *     up destructors functions for each of the types (parts, attribs, etc.).
 *     In another words, we use divide-and-conquer. 
 */


void
objDestroy(ModelObject *obj)
{
  int i, j;

  /* Cube and sphere are basic primitives. */
  /* First of all, cube and sphere are permanent objects because 
     other scenes, such as park.sg, will need to reference cube and 
     sphere geometry.  Devil will break loose if nodes try to reference
     destroyed objects. */
  /* Also, Cube destroy isn't right, yet.  The facets indexes are
     not allocated by malloc thus can't be freed.  Need to use malloc
     instead. */ 

  if (objIsInternal(obj)) {
    return;
  }
  

  /* Ignore those tesselations without destructors. Note we still
   *  need to get rid of them.*/
  if (objIsPrimTess(obj)) {
    zdo6 fprintf(stderr, "Object is tess, ignored\n");
    return;
  }

  triStripCnt = 0;
  fptrCnt = 0;
  edgeCnt = 0;
  flistCnt = 0;
  for(i = 0; i < obj->partCnt; i++)
    partDestroy(obj->parts[i]);
  free(obj->parts);

  /* Clear Facetlists for each vertex first. This is a 1 based array */
  for(i = 0; i < obj->vertCnt+1; i++) {
    linkedListDestroy(obj->varray[i].elist);
    surfDestroy(obj->varray[i].surf);
    flistDestroy(obj->varray[i].facets);
  }
  free(obj->varray); /* bye, bye */
  
  zdo6 fprintf(stderr, "freed %d fptrs, %d edges %d flists, %d triStrips.\n",
	 fptrCnt, edgeCnt, flistCnt, triStripCnt);
  transformDestroy(obj->transform);
  attribDestroy(obj->attrib);
  bboxDestroy(obj->bbox);
  /* Done. Send obj to promised land. */
  free(obj);

}  /* End of objDestroy*/






  

/*
 * addModelObj
 *
 *      Adds a new obj to the list of objects.
 */

void
addModelObj (ModelObject *obj)
{
  int i;
  
  if (obj == NULL)
      return;

#if 0 
  /* Not used. */
  /* First go through each object to replace any that has the same
   * name (except the cube and sphere) 
   */
  for(i = 0; i < UserState.numObjs; i++){
    if ((strcmp(UserState.objs[i]->name, obj->name) == 0) &&
	(strcmp(obj->name, "cube") != 0) && 
	(strcmp(obj->name, "sphere") != 0)){
      strcpy(UserState.objs[i]->name, "replaced");
    }
  }
#endif

#if 0
  zdo6 fprintf(stderr, "Adding obj %s numobjs %d\n", obj->name, 
	      UserState.numObjs+1);
  UserState.currentObjIndex = UserState.numObjs;
  UserState.objs[UserState.numObjs++] = obj;
  UserState.currentObj = obj;
 #endif
}  /* End of addModelObj*/


/*
 * objLookupPart
 *
 *      Looks up a part from obj.
 */

Part *
objLookupPart(ModelObject *obj, char *partName)
{
  int i;
  
  for (i = 0; i < obj->partCnt; i++)
    if (strcmp(obj->parts[i]->name, partName) == 0) 
      return obj->parts[i];
  return NULL;
}  /* End of objLookupPart */
  




/*
 * objGotoCenter
 *
 *      Translate an object to the origin.
 */

void
objGotoCenter (ModelObject *obj)
{
    int i;
    ModelTrans *ms;
    float maxScale;

    ms = obj->transform;
    maxScale = 0.0;
    for (i = 0 ; i < 3; i++){
	ms->center[i] = (obj->bbox->min[i] + obj->bbox->max[i]) / 2.0;
	maxScale = maxf(maxScale, obj->bbox->max[i]);
	maxScale = maxf(maxScale, fabs(obj->bbox->min[i]));
    }
    zdo6 fprintf(stderr, "z %f ", maxScale);
    

    ms->translate[0] = 0.0;
    ms->translate[1] = 0.0;
    ms->translate[2] = - 5.0;

    zdo6 fprintf(stderr, "Scale:\n");
    for (i = 0 ; i < 3; i++){
	ms->scale[i] = 1.0 / maxScale; 
	zdo6 fprintf(stderr, " %f ", ms->scale[i]);
    }

}  /* End of objGotoCenter*/

	    



/*
 * objFindPartBbox
 *
 *     Finds bbox for this part.
 */

void
objFindPartBbox (ModelObject *obj)
{
  int i;
  
  for (i = 0; i < obj->partCnt; i++)
    partFindBbox(obj->parts[i]);
}  /* End of objFindPartBbox */


/*
 * gotNewPart
 *
 *      A part is either an existing part or a new one.  
 * Ignores "default" parts (name is "default).
 */

void
gotNewPart(ModelObject *obj, Part **currPart, char *name)
{
    int i;
    
    /* Ignore "default" part */
    if (strcmp(name, "default") == 0)
	return;
    zdo6 fprintf(stderr, "got new part name:%s\n", name);
    /* A previous part ?*/
    for (i = 0; i < obj->partCnt; i++){
	if (strcmp(obj->parts[i]->name, name) == 0) {
	    *currPart = obj->parts[i];
	    return;
	}
    }
    
    /* No, make a new part */
    *currPart = partCreate(obj);
    (*currPart)->name = genString(name);
    objAddPart(obj, *currPart);
}




/*
 * gotNewVertex
 *
 *      Store a new vertex.
 */

void
gotNewVertex(ModelObject *obj, Point p)
{
    varrayAddNewVertex(&obj->varray, &obj->varraySize, 
		       &obj->vertCnt, p); 
    bboxExtendPoint(obj->bbox, p);
}  /* End of gotNewVertex */

 /* End of types.c*/




