CCL Home Page
Up Directory CCL ScianPictures
/*ScianPictures.c
  Eric Pepke
  June 25, 1990
  Code to draw 3-D pictures in scian.
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianColors.h"
#include "ScianPictures.h"
#include "ScianIDs.h"
#include "ScianErrors.h"
#include "ScianWindows.h"
#include "ScianDatasets.h"
#include "ScianArrays.h"
#include "ScianPreferences.h"
#include "ScianSpaces.h"
#include "ScianVisObjects.h"
#include "ScianPick.h"

static void AppendItemToPicture(PicPtr, PicItemPtr);
static PolyPtr ConvertPolyOntoPolys(PolysPtr polys, PolyPtr poly);

#define MAXCYLSIDES	512
#define CYLSIDES	16	/*Sides around cylinders, must be 2^n, 4 or more*/
#define MAXSUB		8	/*Maximum number of subdivisions*/
#ifndef SPHERESUB
#define SPHERESUB	2	/*Number of subdivisions of a sphere*/
#endif

#ifdef GRAPHICS
Linestyle dashedLine = 0xF0F0;
Linestyle dottedLine = 0xAAAA;
#endif

double cefs[MAXSUB];		/*Chord extension factor for each subdivision*/
double cefs_2[MAXSUB];		/*cefs / 2*/

ObjPtr picClass = 0;		/*Class for all pictures*/

Bool orderReversed = false;	/*True iff polygon order is reversed*/
Bool overrideColor = false;	/*True iff override color*/
int curColorMode;		/*The current color mode, CMODECMAP or CMODERGB*/
int curColorShading;		/*The current color shading*/
int curLightShading;		/*The current light shading*/ 
Bool curIsTransparent;		/*True iff transparent*/

long pictureTime = 0;		/*Picture drawing time*/

VertexPtr freedVertices = 0;	/*Freed vertices for the picking*/

#ifdef GRAPHICS
unsigned short greyPattern[16] = 
	{
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA
	};

unsigned short altGreyPattern[16] = 
	{
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555,
	    0xAAAA,
	    0x5555
	};

unsigned short funkyGreyPattern[16] = 
	{
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA
	};

unsigned short funkyAltGreyPattern[16] = 
	{
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555,
	    0xAAAA,
	    0xAAAA,
	    0x5555,
	    0x5555
	};

unsigned short oddPattern[16] = 
	{
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000
	};

unsigned short evenPattern[16] = 
	{
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF,
	    0x0000,
	    0xFFFF
	};

unsigned short tempPattern[16];

Matrix cardBasis=
    {
        {-0.5,  1.5, -1.5,  0.5},
        { 1.0, -2.5,  2.0, -0.5},
        {-0.5,  0.0,  0.5,  0.0},
        { 0.0,  1.0,  0.0,  0.0}
    };
#endif

typedef struct
    {
	Bool rWrite;
	Bool gWrite;
	Bool bWrite;
	Bool zWrite;
    } WriteMask;

#define NWRITEMASKS	10
static int curWriteMask = 0;

static WriteMask writeMasks[NWRITEMASKS];

#ifdef HAVE_PROTOTYPES
static void SetCurMask(void)
#else
static void SetCurMask()
#endif
/*Sets the mask according to the current mask*/
{
    if (rgbp)
    {
	RGBwritemask(writeMasks[curWriteMask] . rWrite ? 0xff : 0,
		     writeMasks[curWriteMask] . gWrite ? 0xff : 0,
		     writeMasks[curWriteMask] . bWrite ? 0xff : 0);
    }
    zwritemask(writeMasks[curWriteMask] . zWrite ? 0xffffffff : 0);
}

#ifdef HAVE_PROTOTYPES
void BeginMask(Bool rWrite, Bool gWrite, Bool bWrite, Bool zWrite)
#else
void BeginMask(rWrite, gWrite, bWrite, zWrite)
#endif
/*Sets up write masks to r, g, b, and z buffer.  Bracket with EndMask*/
{
    ++curWriteMask;

    writeMasks[curWriteMask] . rWrite = rWrite ? writeMasks[curWriteMask - 1] . rWrite : false;
    writeMasks[curWriteMask] . gWrite = gWrite ? writeMasks[curWriteMask - 1] . gWrite : false;
    writeMasks[curWriteMask] . bWrite = bWrite ? writeMasks[curWriteMask - 1] . bWrite : false;
    writeMasks[curWriteMask] . zWrite = zWrite ? writeMasks[curWriteMask - 1] . zWrite : false;

    SetCurMask();
}

#ifdef HAVE_PROTOTYPES
void EndMask(void)
#else
void EndMask()
#endif
/*End of a BeginMask block*/
{
    --curWriteMask;

    SetCurMask();
}

#ifdef HAVE_PROTOTYPES
void OverrideColor(Bool whether)
#else
void OverrideColor(whether)
Bool whether;
#endif
/*Overrides the colors in a picture*/
{
    overrideColor = whether;
}

static ObjPtr MakePictureDeformed(picture)
ObjPtr picture;
/*Makes a picture's PICDEFORMED variable.  Really doesn't have to do anything*/
{
    SetVar(picture, PICDEFORMED, ObjTrue);
}

#ifdef HAVE_PROTOTYPES
VertexPtr NewVertex(ObjPtr picture, int flags)
#else
VertexPtr NewVertex(picture, flags)
ObjPtr picture;
int flags;
#endif
/*Returns a new vertex, stuck on picture*/
{
    VertexPtr retVal;

    if (freedVertices)
    {
	retVal = freedVertices;
	freedVertices = freedVertices -> next;
    }
    else
    {
	retVal = newp(Vertex);
    }

    if (retVal)
    {
	retVal -> normal[0] = retVal -> normal[1] = 0.0;
	retVal -> normal[2] = 1.0;
	retVal -> position[0] = retVal -> position[1] = retVal -> position[2] = 0.0;
	retVal -> colorIndex = 0;

	retVal -> next = 0;
	retVal -> flags = flags;
	if (((PicPtr) picture) -> lastVertex)
	{
	    (((PicPtr) picture) -> lastVertex) -> next = retVal;
	    ((PicPtr) picture) -> lastVertex = retVal;
	}
	else
	{
	    ((PicPtr) picture) -> lastVertex = retVal;
	    ((PicPtr) picture) -> vertices = retVal;
	}
    }
    return retVal;
}

#ifdef HAVE_PROTOTYPES
VertexPtr InsertVertex(ObjPtr picture, VertexPtr after)
#else
VertexPtr InsertVertex(picture, after)
ObjPtr picture;
VertexPtr after;
#endif
/*Returns a new vertex, stuck on picture after after, with VF_SAMEPLACE*/
{
    VertexPtr retVal;

    if (freedVertices)
    {
	retVal = freedVertices;
	freedVertices = freedVertices -> next;
    }
    else
    {
	retVal = newp(Vertex);
    }

    if (retVal)
    {
	retVal -> normal[0] = retVal -> normal[1] = 0.0;
	retVal -> normal[2] = 1.0;
	retVal -> position[0] = retVal -> position[1] = retVal -> position[2] = 0.0;
	retVal -> colorIndex = 0;

	retVal -> next = after -> next;
	retVal -> flags = VF_SAMEPLACE;

	after -> next = retVal;

	if (((PicPtr) picture) -> lastVertex == after)
	{
	    ((PicPtr) picture) -> lastVertex = retVal;
	}
    }
    return retVal;
}

#ifdef HAVE_PROTOTYPES
void FreeVertex(VertexPtr vertex)
#else
void FreeVertex(vertex)
VertexPtr vertex;
#endif
/*Frees a vertex*/
{
    vertex -> next = freedVertices;
    freedVertices = vertex;
}

static ObjPtr RegisterPicField(pic, whichField)
ObjPtr pic;
int whichField;
/*Registers pic in a field*/
{
    curFields[whichField] . objectInfo = pic;
    return ObjTrue;
}

void InitPictures()
/*Initializes the pictures system*/
{
    int k;
    double theta;

    writeMasks[curWriteMask] . rWrite = true;
    writeMasks[curWriteMask] . gWrite = true;
    writeMasks[curWriteMask] . bWrite = true;
    writeMasks[curWriteMask] . zWrite = true;

#ifdef GRAPHICS
    deflinestyle(DASHEDLINE, dashedLine);
    deflinestyle(DOTTEDLINE, dottedLine);

    /*Define the patterns*/
    defpattern(GREYPAT, 16, greyPattern);
    defpattern(EVENPAT, 16, evenPattern);
    defpattern(ODDPAT, 16, oddPattern);
    defpattern(ALTGREYPAT, 16, altGreyPattern); 

    for (k = 0; k < 16; ++k) tempPattern[k] = funkyGreyPattern[k] & evenPattern[k];
    defpattern(EVENGREYPAT, 16, tempPattern);

    for (k = 0; k < 16; ++k) tempPattern[k] = funkyGreyPattern[k] & oddPattern[k];
    defpattern(ODDGREYPAT, 16, tempPattern);

    for (k = 0; k < 16; ++k) tempPattern[k] = funkyAltGreyPattern[k] & evenPattern[k];
    defpattern(EVENALTGREYPAT, 16, tempPattern);

    for (k = 0; k < 16; ++k) tempPattern[k] = funkyAltGreyPattern[k] & oddPattern[k];
    defpattern(ODDALTGREYPAT, 16, tempPattern);

    /*Define the cardinal spline basis*/
    defbasis(CARDBASIS, cardBasis);
#endif

    picClass = NewObject(NULLOBJ, 0);
    AddToReferenceList(picClass);
    SetMethod(picClass, REGISTERFIELD, RegisterPicField);

    DeclareIndirectDependency(picClass, PICDEFORMED, REPOBJ, PICDEFORMED);
    SetMethod(picClass, PICDEFORMED, MakePictureDeformed);

    /*Initialize cef*/
    theta = M_PI_4;
    for (k = 0; k < MAXSUB; ++k)
    {
	cefs[k] = 1.0 / rcos(theta);
	cefs_2[k] = cefs[k] * 0.5;
	theta *= 0.5;
    }
}

void KillPictures()
/*Kills the pictures system*/
{
    DeleteThing(picClass);
}

static ObjPtr CleanupPicture(pic)
PicPtr pic;
/*Cleans up pic*/
{
    while (pic -> vertices)
    {
	VertexPtr next;
	next = pic -> vertices -> next;
	Free(pic -> vertices);
	pic -> vertices = next;
    }
    pic -> lastVertex = 0;

    while (pic -> items)
    {
	PicItemPtr next;
	if (pic -> items -> type == POLYGONS)
	{
	    PolyPtr runner, nr;
	    runner = ((PolysPtr) pic -> items) -> polygons;
	    while (runner)
	    {
		nr = (PolyPtr) runner -> item . next;
		Free(runner);
		runner = nr;
	    }
	}
	next = pic -> items -> next;
	Free(pic -> items);
	pic -> items = next;
    }
    pic -> lastItem = 0;
    return ObjTrue;
}

ObjPtr NewPicture()
/*Returns a new empty picture or 0 if it could not make one.*/
{
    PicPtr retVal;
    retVal = (PicPtr) NewObject(picClass, sizeof(Picture) - sizeof(Thing));
    if (retVal)
    {
        SETOBJTYPE(retVal -> thing . flags, PICTURE);
	SetMethod((ObjPtr) retVal, CLEANUP, CleanupPicture);
	retVal -> vertices = 0;
	retVal -> lastVertex = 0;

	retVal -> items = 0;
	retVal -> lastItem = 0;
	return (ObjPtr) retVal;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolysPtr AppendPolysToPicture(ObjPtr picture)
#else
PolysPtr AppendPolysToPicture(picture)
ObjPtr picture;
#endif
/*Returns a new empty set of polygons 0 if it could not make one.*/
{
    PolysPtr item;
    item = (PolysPtr) Alloc(sizeof(Polygons));
    if (item)
    {
	item -> item . type = POLYGONS;
	item -> item . proximity = 0;
	item -> item . flags = 0;

	item -> picture = (PicPtr) picture;
	item -> polygons = 0;
	item -> lastItem = 0;
	item -> enclosed = false;

	AppendItemToPicture((PicPtr) picture, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolyPtr AppendPolyToPolys(PolysPtr polys, long nVertices, Vertex vertices[])
#else
PolyPtr AppendPolyToPolys(polys, nVertices, vertices)
PolysPtr polys;
long nVertices;
Vertex vertices[];
#endif
/*Append a polygon with nVertices where vertices is an array of Vertex
  to polygons polys.  Returns true iff successful.*/
{
    PolyPtr item;
    int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYGON;
	item -> item . proximity = 0;
	item -> item . flags = 0;

	item -> nVertices = nVertices;
	for (k = 0; k < nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex((ObjPtr) polys -> picture, 0);
	    for (d = 0; d < 3; ++d)
	    {
		item -> vertices[k] -> position[d] = vertices[k] . position[d];
		item -> vertices[k] -> normal[d] = vertices[k] . normal[d];
	    }
	    item -> vertices[k] -> colorIndex = vertices[k] . colorIndex;
	}
	if (polys -> polygons)
	{
	    /*It's not the first*/
	    polys -> lastItem -> item . next = (PicItemPtr) item;
	}
	else
	{
	    /*It is the first*/
	    polys -> polygons = item;
	}
	item -> item . next = 0;
	polys -> lastItem = item;
	return item;
    }
    else
    {
	OMErr();
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolyPtr AppendSPolyToPolys(PolysPtr polys, long nVertices, VertexPtr vertices[])
#else
PolyPtr AppendSPolyToPolys(polys, nVertices, vertices)
PolysPtr polys;
long nVertices;
VertexPtr vertices[];
#endif
/*Append a polygon with nVertices where vertices is an array of VertexPtr
  to polygons polys.  Returns true iff successful.  The vertices are 
  presumed to be shared.*/
{
    PolyPtr item;
    int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYGON;
	item -> item . proximity = 0;
	item -> item . flags = 0;

	item -> nVertices = nVertices;
	for (k = 0; k < nVertices; ++k)
	{
	    item -> vertices[k] = vertices[k];
	    for (d = 0; d < 3; ++d)
	    {
		item -> vertices[k] -> position[d] = vertices[k] -> position[d];
		item -> vertices[k] -> normal[d] = vertices[k] -> normal[d];
	    }
	    item -> vertices[k] -> colorIndex = vertices[k] -> colorIndex;
	}
	if (polys -> polygons)
	{
	    /*It's not the first*/
	    polys -> lastItem -> item . next = (PicItemPtr) item;
	}
	else
	{
	    /*It is the first*/
	    polys -> polygons = item;
	}
	item -> item . next = 0;
	polys -> lastItem = item;
	return item;
    }
    else
    {
	OMErr();
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolyPtr TesselateSPolyToPicture(ObjPtr pic, long nVertices, VertexPtr vertices[])
#else
PolyPtr TesselateSPolyToPicture(pic, nVertices, vertices)
ObjPtr pic;
long nVertices;
VertexPtr vertices[];
#endif
/*Tesselates a polygon with nVertices where vertices is an array of VertexPtr
  to picture pic.  Returns the resulting item iff successful.  The vertices are 
  presumed to be shared.  It's a very simple-minded tesselation, not guaranteed
  to work for non convex polygons*/
{
    PolyPtr item;
    int k, d;
    register VertexPtr *v1, *v2;

    v1 = vertices;
    v2 = vertices + nVertices - 1;


    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYTRI;
	item -> item . proximity = 0;
	item -> item . flags = 0;

	item -> nVertices = nVertices;

	/*Fill the vertices*/
	k = 0;

	item -> vertices[k++] = *(v1++);
	while (v2 >= v1)
	{
	    item -> vertices[k++] = *(v1++);
	    if (v2 >= v1)
	    {
		item -> vertices[k++] = *(v2--);
	    }
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	OMErr();
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
static PolyPtr ConvertPolyOntoPolys(PolysPtr polys, PolyPtr poly)
#else
static PolyPtr ConvertPolyOntoPolys(polys, poly)
PolysPtr polys;
PolyPtr poly;
#endif
/*Append a polygon with nVertices where vertices is an array of Vertex
  to polygons polys.  Returns true iff successful.*/
{
    PolyPtr item;
    int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (poly -> nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYGON;
	item -> item . proximity = poly -> item . proximity;
	item -> item . flags = 0;

	item -> nVertices = poly -> nVertices;
	for (k = 0; k < poly -> nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex((ObjPtr) polys -> picture, 0);
	    for (d = 0; d < 3; ++d)
	    {
		item -> vertices[k] -> position[d] =
			poly -> vertices[k] -> position[d];
		item -> vertices[k] -> normal[d] =
			poly -> vertices[k] -> normal[d];
	    }
	    item -> vertices[k] -> colorIndex =
		poly -> vertices[k] -> colorIndex;
	}
	if (polys -> polygons)
	{
	    /*It's not the first*/
	    polys -> lastItem -> item . next = (PicItemPtr) item;
	}
	else
	{
	    /*It is the first*/
	    polys -> polygons = item;
	}
	item -> item . next = 0;
	polys -> lastItem = item;
	return item;
    }
    else
    {
	OMErr();
	return 0;
    }
}

static void AppendItemToPicture(pic, item)
PicPtr pic;
PicItemPtr item;
/*Appends item to the end of pic*/
{
    if (pic -> items)
    {
	/*It's not the first*/
	pic -> lastItem -> next = item;
    }
    else
    {
	/*It is the first*/
	pic -> items = item;
    }
    item -> next = 0;
    pic -> lastItem = item;
}

#ifdef HAVE_PROTOTYPES
RectMeshPtr AppendRectMeshToPicture(ObjPtr picture, long xDim, long yDim, Bool inCenter)
#else
RectMeshPtr AppendRectMeshToPicture(picture, xDim, yDim, inCenter)
ObjPtr picture;
long yDim, xDim;
Bool inCenter;
#endif
/*Appends a new rectangular mesh.  inCenter if nodes in center*/
{
    RectMeshPtr item;
    long nVertices;
    long k;
    Bool first;

    nVertices = (xDim * (2 * yDim - 1));
    item = (RectMeshPtr)
	   Alloc(sizeof(RectMesh) + (nVertices - 1) *
			sizeof(VertexPtr));

    if (inCenter)
    {
	/*Make the nodes with the center nodes canonical*/
	item -> vertices[0] = NewVertex(picture, 0);
	item -> vertices[1] = NewVertex(picture, VF_FIRSTCANON);
	for (k = 2; k < nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex(picture, (k & 1) ? VF_NEXTCANON : 0);
	}
    }
    else
    {
	/*Make as if vertex nodes are canonical*/
	item -> vertices[0] = NewVertex(picture, VF_FIRSTCANON);
	for (k = 1; k < nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex(picture, (k & 1) ? 0 : VF_NEXTCANON);
	}
    }

    item -> item . type = RECTMESH;
    item -> item . proximity = 0;
    item -> item . flags = 0;

    item -> inCenter = inCenter;
    item -> xDim = xDim;
    item -> yDim = yDim;

    AppendItemToPicture((PicPtr) picture, (PicItemPtr) item);
    return item;
}

#ifdef HAVE_PROTOTYPES
VertexPtr RectMeshVertex(RectMeshPtr rectMesh, long i, long j)
#else
VertexPtr RectMeshVertex(rectMesh, i, j)
RectMeshPtr rectMesh;
long i, j;
#endif
/*Returns vertex i, j of a rect mesh*/
{
    long offset;
    offset = RECTMESHVERTEX(rectMesh, i, j);
    return rectMesh -> vertices[offset]; 
}

#ifdef HAVE_PROTOTYPES
VertexPtr RectMeshCenter(RectMeshPtr rectMesh, long i, long j)
#else
VertexPtr RectMeshCenter(rectMesh, i, j)
RectMeshPtr rectMesh;
long i, j;
#endif
/*Returns center i, j of a rect mesh*/
{
    long offset;
    offset = RECTMESHCENTER(rectMesh, i, j);
    return rectMesh -> vertices[offset]; 
}

#define ISVECZERO(v) (((v)[0] == 0.0) && ((v)[1] == 0.0) && ((v)[2] == 0.0))

#ifdef HAVE_PROTOTYPES
void CalcRectNormals(RectMeshPtr rectMesh)
#else
void CalcRectNormals(rectMesh)
RectMeshPtr rectMesh;
#endif
/*Calculates the normals in rectMesh and copies them to the vertices.
  Obviously, do this after the positions have been set.*/
{
    register long i, j, k, offset, offset2, offset3, iDim, jDim, nX;
    register VertexPtr *vertices;
    register long nVertices;
    float normal[3];		/*Normal at center vertex*/
    float position1[3];		/*Position of base vertex*/
    float vI[3], vJ[3];		/*Vectors for cross product*/
    float vI0[3], vI1[3], vJ0[3], vJ1[3];
    float vt[3];		/*Test vertex*/
    float cn[3];		/*Candidate normal*/

    iDim = rectMesh -> xDim;
    jDim = rectMesh -> yDim;
    vertices = rectMesh -> vertices;
    nVertices = (iDim * (2 * jDim - 1));
    nX = jDim * 2 - 1;

#define nY 2

#ifdef OLDRECTNORMALS
    /*Make all the _vertex_ normals*/
    for (j = 0; j < jDim; ++j)
    {
	for (i = 0; i < iDim; ++i)
	{
	    vI[0] = vI[1] = vI[2] = 0.0;
	    vJ[0] = vJ[1] = vJ[2] = 0.0;

	    offset = RECTMESHVERTEX(rectMesh, i, j);

	    /*Get position here at vertex*/
	    position1[0] = vertices[offset] -> position[0];
	    position1[1] = vertices[offset] -> position[1];
	    position1[2] = vertices[offset] -> position[2];

	    /*Make i component*/
	    if (i >= iDim - 1)
	    {
		/*Get from i - 1*/
		offset2 = offset - nX;
		vI[0] += position1[0] - vertices[offset2] -> position[0];
		vI[1] += position1[1] - vertices[offset2] -> position[1];
		vI[2] += position1[2] - vertices[offset2] -> position[2];
	    }
	    else if (i <= 0)
	    {
		/*Get from i + 1*/
		offset2 = offset + nX;
		vI[0] += vertices[offset2] -> position[0] - position1[0];
		vI[1] += vertices[offset2] -> position[1] - position1[1];
		vI[2] += vertices[offset2] -> position[2] - position1[2];
	    }
	    else
	    {
		/*Get from i + 1 and i - 1*/
		offset2 = offset - nX;
		offset3 = offset + nX;
		vI[0] += vertices[offset3] -> position[0] - vertices[offset2] -> position[0];
		vI[1] += vertices[offset3] -> position[1] - vertices[offset2] -> position[1];
		vI[2] += vertices[offset3] -> position[2] - vertices[offset2] -> position[2];
	    }

	    /*Make j component*/
	    if (j >= jDim - 1)
	    {
		/*Get from j - 1*/
		offset2 = offset - nY;
		vJ[0] += position1[0] - vertices[offset2] -> position[0];
		vJ[1] += position1[1] - vertices[offset2] -> position[1];
		vJ[2] += position1[2] - vertices[offset2] -> position[2];
	    }
	    else if (j <= 0)
	    {
		/*Get from j + 1*/
		offset2 = offset + nY;
		vJ[0] += vertices[offset2] -> position[0] - position1[0];
		vJ[1] += vertices[offset2] -> position[1] - position1[1];
		vJ[2] += vertices[offset2] -> position[2] - position1[2];
	    }
	    else
	    {
		/*Get from j + 1 and j - 1*/
		offset2 = offset - nY;
		offset3 = offset + nY;
		vJ[0] += vertices[offset3] -> position[0] - vertices[offset2] -> position[0];
		vJ[1] += vertices[offset3] -> position[1] - vertices[offset2] -> position[1];
		vJ[2] += vertices[offset3] -> position[2] - vertices[offset2] -> position[2];
	    }

	    /*Cross i by j to get normal*/
	    CROSS(vI, vJ, normal);

	    if (normal[0] == 0.0 && normal[1] == 0.0 && normal[2] == 0.0)
	    {
		normal[2] = 1.0;
	    }

	    /*Normalize it*/
	    NORMALIZE(normal);
	    vertices[offset] -> normal[0] += normal[0];
	    vertices[offset] -> normal[1] += normal[1];
	    vertices[offset] -> normal[2] += normal[2];
	}
    }

    /*Interpolate the centers with four corners*/
    for (i = 1; i < iDim - 1; ++i)
    {
	for (j = 1; j < jDim - 1; ++j)
	{
	    /*Lower left corner*/
	    offset = RECTMESHVERTEX(rectMesh, i - 1, j - 1);
	    normal[0] = vertices[offset] -> normal[0];
	    normal[1] = vertices[offset] -> normal[1];
	    normal[2] = vertices[offset] -> normal[2];

	    /*Upper left corner*/
	    offset = RECTMESHVERTEX(rectMesh, i - 1, j);
	    normal[0] += vertices[offset] -> normal[0];
	    normal[1] += vertices[offset] -> normal[1];
	    normal[2] += vertices[offset] -> normal[2];

	    /*Lower right corner*/
	    offset = RECTMESHVERTEX(rectMesh, i, j - 1);
	    normal[0] += vertices[offset] -> normal[0];
	    normal[1] += vertices[offset] -> normal[1];
	    normal[2] += vertices[offset] -> normal[2];

	    /*Upper right corner*/
	    offset = RECTMESHVERTEX(rectMesh, i, j);
	    normal[0] += vertices[offset] -> normal[0];
	    normal[1] += vertices[offset] -> normal[1];
	    normal[2] += vertices[offset] -> normal[2];

	    if (normal[0] == 0.0 && normal[1] == 0.0 && normal[2] == 0.0)
	    {
		normal[2] = 1.0;
	    }

	    /*Normalize it*/
	    NORMALIZE(normal);

	    /*Stuff them*/
	    offset = RECTMESHCENTER(rectMesh, i, j);
	    vertices[offset] -> normal[0] += normal[0];
	    vertices[offset] -> normal[1] += normal[1];
	    vertices[offset] -> normal[2] += normal[2];
	}
    }

#else
    /*Zero all the vertices*/
    for (k = 0; k < nVertices; ++k)
    {
	vertices[k] -> normal[0] = 0.0;
	vertices[k] -> normal[1] = 0.0;
	vertices[k] -> normal[2] = 0.0;
    }

    /*Make all the center normals from edges*/
    for (j = 0; j < jDim - 1; ++j)
    {
	for (i = 0; i < iDim - 1; ++i)
	{
	    offset = RECTMESHVERTEX(rectMesh, i, j);

	    /*Get position here at vertex*/
	    position1[0] = vertices[offset] -> position[0];
	    position1[1] = vertices[offset] -> position[1];
	    position1[2] = vertices[offset] -> position[2];

	    /*Make i0 component*/
	    offset2 = offset + nX;
	    vI0[0] = vertices[offset2] -> position[0] - position1[0];
	    vI0[1] = vertices[offset2] -> position[1] - position1[1];
	    vI0[2] = vertices[offset2] -> position[2] - position1[2];

	    /*Make j0 component*/
	    offset2 = offset + nY;
	    vJ0[0] = vertices[offset2] -> position[0] - position1[0];
	    vJ0[1] = vertices[offset2] -> position[1] - position1[1];
	    vJ0[2] = vertices[offset2] -> position[2] - position1[2];

	    /*Get position at catty corner vertex*/
	    offset = offset + nX + nY;
	    position1[0] = vertices[offset] -> position[0];
	    position1[1] = vertices[offset] -> position[1];
	    position1[2] = vertices[offset] -> position[2];

	    /*Make i1 component*/
	    offset2 = offset - nX;
	    vI1[0] = position1[0] - vertices[offset2] -> position[0];
	    vI1[1] = position1[1] - vertices[offset2] -> position[1];
	    vI1[2] = position1[2] - vertices[offset2] -> position[2];

	    /*Make j1 component*/
	    offset2 = offset - nY;
	    vJ1[0] = position1[0] - vertices[offset2] -> position[0];
	    vJ1[1] = position1[1] - vertices[offset2] -> position[1];
	    vJ1[2] = position1[2] - vertices[offset2] -> position[2];


	    /*Now use the four candidates to get an approximate normal*/
	    normal[0] = normal[1] = normal[2] = 0.0;
	    CROSS(vI0, vJ0, cn);
	    normal[0] += cn[0];
	    normal[1] += cn[1];
	    normal[2] += cn[2];
	    CROSS(vI1, vJ0, cn);
	    normal[0] += cn[0];
	    normal[1] += cn[1];
	    normal[2] += cn[2];
	    CROSS(vI0, vJ1, cn);
	    normal[0] += cn[0];
	    normal[1] += cn[1];
	    normal[2] += cn[2];
	    CROSS(vI1, vJ1, cn);
	    normal[0] += cn[0];
	    normal[1] += cn[1];
	    normal[2] += cn[2];

	    if (normal[0] != 0.0 || normal[1] != 0.0 || normal[2] != 0.0)
	    {
		/*It isn't degenerate.  Normalize it.*/
		NORMALIZE(normal);

		/*Set it*/
		offset = RECTMESHCENTER(rectMesh, i, j);
		vertices[offset] -> normal[0] = normal[0];
		vertices[offset] -> normal[1] = normal[1];
		vertices[offset] -> normal[2] = normal[2];
	    }
	    /*If not set, a center will be considered degenerate.*/
	}
    }

    /*Make all the vertex normals from center*/
    for (j = 0; j < jDim - 1; ++j)
    {
	for (i = 0; i < iDim - 1; ++i)
	{
	    /*Get pointer for this vertex*/
	    offset = RECTMESHCENTER(rectMesh, i, j);

	    /*Get temporary normal*/
	    normal[0] = vertices[offset] -> normal[0];
	    normal[1] = vertices[offset] -> normal[1];
	    normal[2] = vertices[offset] -> normal[2];

	    /*Get pointer for lower left center*/
	    offset2 = RECTMESHVERTEX(rectMesh, i, j);

	    /*Distribute this normal to all four corners*/

	    vertices[offset2] -> normal[0] += normal[0];
	    vertices[offset2] -> normal[1] += normal[1];
	    vertices[offset2] -> normal[2] += normal[2];

	    offset2 += nX; 

	    vertices[offset2] -> normal[0] += normal[0];
	    vertices[offset2] -> normal[1] += normal[1];
	    vertices[offset2] -> normal[2] += normal[2];

	    offset2 += nY; 

	    vertices[offset2] -> normal[0] += normal[0];
	    vertices[offset2] -> normal[1] += normal[1];
	    vertices[offset2] -> normal[2] += normal[2];

	    offset2 -= nX; 
	    vertices[offset2] -> normal[0] += normal[0];
	    vertices[offset2] -> normal[1] += normal[1];
	    vertices[offset2] -> normal[2] += normal[2];
	}
    }

    /*Go through and fix degenerate normals*/
    for (k = 0; k < nVertices; ++k)
    {
	if (ISVECZERO(vertices[k] -> normal))
	{
	    vertices[k] -> normal[2] = 1.0;
	}
	else
	{
	    NORMALIZE(vertices[k] -> normal);
	}
    }
#endif

#undef nY

}

#ifdef HAVE_PROTOTYPES
void InterpRectCenters(RectMeshPtr rectMesh)
#else
void InterpRectCenters(rectMesh)
RectMeshPtr rectMesh;
#endif
/*Interpolates rect centers from vertices*/
{
    register long i, j, offset, iDim, jDim, nX;
    register VertexPtr *vertices;
    float position[3];		/*Position of center vertex*/
    int colorIndex;		/*Color within color table*/

    iDim = rectMesh -> xDim;
    jDim = rectMesh -> yDim;
    vertices = rectMesh -> vertices;
    nX = jDim * 2 - 1;

    for (i = 0; i < iDim - 1; ++i)
    {
	for (j = 0; j < jDim - 1; ++j)
	{
	    /*Lower left corner*/
	    offset = RECTMESHVERTEX(rectMesh, i, j);
	    position[0] = vertices[offset] -> position[0];
	    position[1] = vertices[offset] -> position[1];
	    position[2] = vertices[offset] -> position[2];
	    colorIndex = vertices[offset] -> colorIndex;

	    /*Upper left corner*/
	    offset = RECTMESHVERTEX(rectMesh, i, j + 1);
	    position[0] += vertices[offset] -> position[0];
	    position[1] += vertices[offset] -> position[1];
	    position[2] += vertices[offset] -> position[2];
	    colorIndex += vertices[offset] -> colorIndex;

	    /*Lower right corner*/
	    offset = RECTMESHVERTEX(rectMesh, i + 1, j);
	    position[0] += vertices[offset] -> position[0];
	    position[1] += vertices[offset] -> position[1];
	    position[2] += vertices[offset] -> position[2];
	    colorIndex += vertices[offset] -> colorIndex;

	    /*Upper right corner*/
	    offset = RECTMESHVERTEX(rectMesh, i + 1, j + 1);
	    position[0] += vertices[offset] -> position[0];
	    position[1] += vertices[offset] -> position[1];
	    position[2] += vertices[offset] -> position[2];
	    colorIndex += vertices[offset] -> colorIndex;

	    /*Normalize results*/
	    position[0] *= 0.25;
	    position[1] *= 0.25;
	    position[2] *= 0.25;
	    colorIndex /= 4;

	    /*Stuff them*/
	    offset = RECTMESHCENTER(rectMesh, i, j);
	    vertices[offset] -> position[0] = position[0];
	    vertices[offset] -> position[1] = position[1];
	    vertices[offset] -> position[2] = position[2];
	    vertices[offset] -> colorIndex = colorIndex;
	}
    }
}

#ifdef HAVE_PROTOTYPES
void InterpRectVertices(RectMeshPtr rectMesh)
#else
void InterpRectVertices(rectMesh)
RectMeshPtr rectMesh;
#endif
/*Interpolates and extrapolates rect vertices from centers*/
{
    register long i, j, k, offset, iDim, jDim, nX;
    register VertexPtr *vertices;
    float position[3];		/*Position of center vertex*/
    int colorIndex;		/*Color within color table*/
    int nColorIndices;

    iDim = rectMesh -> xDim;
    jDim = rectMesh -> yDim;

    vertices = rectMesh -> vertices;
    nX = jDim * 2 - 1;

    /*Interpolate the centers with four corners*/
    for (i = 1; i < iDim - 1; ++i)
    {
	for (j = 1; j < jDim - 1; ++j)
	{
	    nColorIndices = 0;

	    /*Lower left corner*/
	    offset = RECTMESHCENTER(rectMesh, i - 1, j - 1);
	    position[0] = vertices[offset] -> position[0];
	    position[1] = vertices[offset] -> position[1];
	    position[2] = vertices[offset] -> position[2];
	    colorIndex = vertices[offset] -> colorIndex;
	    if (vertices[offset] -> colorIndex) ++nColorIndices;

	    /*Upper left corner*/
	    offset = RECTMESHCENTER(rectMesh, i - 1, j);
	    position[0] += vertices[offset] -> position[0];
	    position[1] += vertices[offset] -> position[1];
	    position[2] += vertices[offset] -> position[2];
	    colorIndex += vertices[offset] -> colorIndex;
	    if (vertices[offset] -> colorIndex) ++nColorIndices;

	    /*Lower right corner*/
	    offset = RECTMESHCENTER(rectMesh, i, j - 1);
	    position[0] += vertices[offset] -> position[0];
	    position[1] += vertices[offset] -> position[1];
	    position[2] += vertices[offset] -> position[2];
	    colorIndex += vertices[offset] -> colorIndex;
	    if (vertices[offset] -> colorIndex) ++nColorIndices;

	    /*Upper right corner*/
	    offset = RECTMESHCENTER(rectMesh, i, j);
	    position[0] += vertices[offset] -> position[0];
	    position[1] += vertices[offset] -> position[1];
	    position[2] += vertices[offset] -> position[2];
	    colorIndex += vertices[offset] -> colorIndex;
	    if (vertices[offset] -> colorIndex) ++nColorIndices;

	    /*Normalize results*/
	    position[0] *= 0.25;
	    position[1] *= 0.25;
	    position[2] *= 0.25;
#if 1
	    if (nColorIndices) colorIndex /= nColorIndices;
	    printf("%d\n", nColorIndices);
#else
	    colorIndex /= 4;
#endif

	    /*Stuff them*/
	    offset = RECTMESHVERTEX(rectMesh, i, j);
	    vertices[offset] -> position[0] = position[0];
	    vertices[offset] -> position[1] = position[1];
	    vertices[offset] -> position[2] = position[2];
	    vertices[offset] -> colorIndex = colorIndex;
	}
    }

    /*Extrapolate left and right sides*/
    for (j = 1; j < jDim - 1; ++j)
    {
	/*Left side*/
	offset = RECTMESHVERTEX(rectMesh, 1, j);
	for (k = 0; k < 3; ++k)
	{
	    position[k] = vertices[offset] -> position[k];
	}
	colorIndex = vertices[offset] -> colorIndex;
	if (iDim >= 3)
	{
	    /*Extend position only*/
	    offset = RECTMESHVERTEX(rectMesh, 2, j);
	    for (k = 0; k < 3; ++k)
	    {
		position[k] += position[k] - vertices[offset] -> position[k];
	    }
	}
	/*Stuff them back*/
	offset = RECTMESHVERTEX(rectMesh, 0, j);
	for (k = 0; k < 3; ++k)
	{
	    vertices[offset] -> position[k] = position[k];
	}
	vertices[offset] -> colorIndex = colorIndex;

	/*Right side*/
	offset = RECTMESHVERTEX(rectMesh, iDim - 2, j);
	for (k = 0; k < 3; ++k)
	{
	    position[k] = vertices[offset] -> position[k];
	}
	colorIndex = vertices[offset] -> colorIndex;
	if (iDim >= 3)
	{
	    /*Extend position only*/
	    offset = RECTMESHVERTEX(rectMesh, iDim - 3, j);
	    for (k = 0; k < 3; ++k)
	    {
		position[k] += position[k] - vertices[offset] -> position[k];
	    }
	}
	/*Stuff them back*/
	offset = RECTMESHVERTEX(rectMesh, iDim - 1, j);
	for (k = 0; k < 3; ++k)
	{
	    vertices[offset] -> position[k] = position[k];
	}
	vertices[offset] -> colorIndex = colorIndex;
    }	

    /*Extrapolate bottom and top sides*/
    for (i = 1; i < iDim - 1; ++i)
    {
	/*Left side*/
	offset = RECTMESHVERTEX(rectMesh, i, 1);
	for (k = 0; k < 3; ++k)
	{
	    position[k] = vertices[offset] -> position[k];
	}
	colorIndex = vertices[offset] -> colorIndex;
	if (jDim >= 3)
	{
	    /*Extend position only*/
	    offset = RECTMESHVERTEX(rectMesh, i, 2);
	    for (k = 0; k < 3; ++k)
	    {
		position[k] += position[k] - vertices[offset] -> position[k];
	    }
	}
	/*Stuff them back*/
	offset = RECTMESHVERTEX(rectMesh, i, 0);
	for (k = 0; k < 3; ++k)
	{
	    vertices[offset] -> position[k] = position[k];
	}
	vertices[offset] -> colorIndex = colorIndex;

	/*Right side*/
	offset = RECTMESHVERTEX(rectMesh, i, jDim - 2);
	for (k = 0; k < 3; ++k)
	{
	    position[k] = vertices[offset] -> position[k];
	}
	colorIndex = vertices[offset] -> colorIndex;
	if (jDim >= 3)
	{
	    /*Extend position only*/
	    offset = RECTMESHVERTEX(rectMesh, i, jDim - 3);
	    for (k = 0; k < 3; ++k)
	    {
		position[k] += position[k] - vertices[offset] -> position[k];
	    }
	}
	/*Stuff them back*/
	offset = RECTMESHVERTEX(rectMesh, i, jDim - 1);
	for (k = 0; k < 3; ++k)
	{
	    vertices[offset] -> position[k] = position[k];
	}
	vertices[offset] -> colorIndex = colorIndex;
    }

    /*Do lower left corner*/
    offset = RECTMESHCENTER(rectMesh, 0, 0);
    for (k = 0; k < 3; ++k)
    {
	position[k] = vertices[offset] -> position[k];
    }
    colorIndex = vertices[offset] -> colorIndex;

    offset = RECTMESHVERTEX(rectMesh, 1, 1);
    for (k = 0; k < 3; ++k)
    {
	position[k] += position[k] - vertices[offset] -> position[k];
    }
    offset = RECTMESHVERTEX(rectMesh, 0, 0);
    for (k = 0; k < 3; ++k)
    {
	vertices[offset] -> position[k] = position[k];
    }
    vertices[offset] -> colorIndex = colorIndex;

    /*Do upper left corner*/
    offset = RECTMESHCENTER(rectMesh, 0, jDim - 2);
    for (k = 0; k < 3; ++k)
    {
	position[k] = vertices[offset] -> position[k];
    }
    colorIndex = vertices[offset] -> colorIndex;

    offset = RECTMESHVERTEX(rectMesh, 1, jDim - 2);
    for (k = 0; k < 3; ++k)
    {
	position[k] += position[k] - vertices[offset] -> position[k];
    }
    offset = RECTMESHVERTEX(rectMesh, 0, jDim - 1);
    for (k = 0; k < 3; ++k)
    {
	vertices[offset] -> position[k] = position[k];
    }
    vertices[offset] -> colorIndex = colorIndex;

    /*Do lower right corner*/
    offset = RECTMESHCENTER(rectMesh, iDim - 2, 0);
    for (k = 0; k < 3; ++k)
    {
	position[k] = vertices[offset] -> position[k];
    }
    colorIndex = vertices[offset] -> colorIndex;

    offset = RECTMESHVERTEX(rectMesh, iDim - 2, 1);
    for (k = 0; k < 3; ++k)
    {
	position[k] += position[k] - vertices[offset] -> position[k];
    }
    offset = RECTMESHVERTEX(rectMesh, iDim - 1, 0);
    for (k = 0; k < 3; ++k)
    {
	vertices[offset] -> position[k] = position[k];
    }
    vertices[offset] -> colorIndex = colorIndex;

    /*Do upper left corner*/
    offset = RECTMESHCENTER(rectMesh, iDim - 2, jDim - 2);
    for (k = 0; k < 3; ++k)
    {
	position[k] = vertices[offset] -> position[k];
    }
    colorIndex = vertices[offset] -> colorIndex;

    offset = RECTMESHVERTEX(rectMesh, iDim - 2, jDim - 2);
    for (k = 0; k < 3; ++k)
    {
	position[k] += position[k] - vertices[offset] -> position[k];
    }
    offset = RECTMESHVERTEX(rectMesh, iDim - 1, jDim - 1);
    for (k = 0; k < 3; ++k)
    {
	vertices[offset] -> position[k] = position[k];
    }
    vertices[offset] -> colorIndex = colorIndex;
}

#ifdef HAVE_PROTOTYPES
PolyPtr AppendPolyToPicture(ObjPtr pic, long nVertices, Vertex vertices[])
#else
PolyPtr AppendPolyToPicture(pic, nVertices, vertices)
ObjPtr pic;
long nVertices;
Vertex vertices[];
#endif
/*Append a polygon with nVertices where vertices is an array of Vertex
  to picture pic.  Returns polygon iff successful.*/
{
    PolyPtr item;
    int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYGON;
	item -> item . proximity = 0;
	item -> item . flags = 0;

	item -> nVertices = nVertices;
	for (k = 0; k < nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex(pic, 0);
	    for (d = 0; d < 3; ++d)
	    {
		item -> vertices[k] -> position[d] = vertices[k] . position[d];
		item -> vertices[k] -> normal[d] = vertices[k] . normal[d];
	    }
	    item -> vertices[k] -> colorIndex = vertices[k] . colorIndex;
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
static PolyPtr ConvertPolyOntoPicture(ObjPtr pic, PolyPtr poly)
#else
static PolyPtr ConvertPolyOntoPicture(pic, poly)
ObjPtr pic;
PolyPtr poly;
#endif
/*Converts a polygon or polyline onto picture.*/
{
    PolyPtr item;
    int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (poly -> nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = poly -> item . type;
	item -> lineWidth = poly -> lineWidth;
	item -> item . proximity = poly -> item . proximity;
	item -> item . flags = 0;

	item -> nVertices = poly -> nVertices;
	for (k = 0; k < poly -> nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex(pic, 0);
	    for (d = 0; d < 3; ++d)
	    {
		item -> vertices[k] -> position[d] =
			poly -> vertices[k] -> position[d];
		item -> vertices[k] -> normal[d] =
			poly -> vertices[k] -> normal[d];
	    }
	    item -> vertices[k] -> colorIndex =
		poly -> vertices[k] -> colorIndex;
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolyPtr AppendPolylineToPicture(ObjPtr pic, int lineWidth, int proximity, long nVertices, Vertex vertices[])
#else
PolyPtr AppendPolylineToPicture(pic, lineWidth, proximity, nVertices, vertices)
ObjPtr pic;
int lineWidth;
int proximity;
long nVertices;
Vertex vertices[];
#endif
/*Append a polyline with nVertices where vertices is an array of Vertex
  to picture pic.  proximity is extra proximity for lines that are to
  go above something.  Returns true iff successful.*/
{
    PolyPtr item;
    int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYLINE;
	item -> item . proximity = proximity;
	item -> item . flags = 0;

	item -> lineWidth = lineWidth;
	item -> nVertices = nVertices;
	for (k = 0; k < nVertices; ++k)
	{
	    item -> vertices[k] = NewVertex(pic, 0);
	    for (d = 0; d < 3; ++d)
	    {
		item -> vertices[k] -> position[d] = vertices[k] . position[d];
		item -> vertices[k] -> normal[d] = vertices[k] . normal[d];
	    }
	    item -> vertices[k] -> colorIndex = vertices[k] . colorIndex;
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolyPtr AppendSPolylineToPicture(ObjPtr pic, int lineWidth, int proximity, long nVertices, VertexPtr vertices[])
#else
PolyPtr AppendSPolylineToPicture(pic, lineWidth, proximity, nVertices, vertices)
ObjPtr pic;
int lineWidth;
int proximity;
long nVertices;
VertexPtr vertices[];
#endif
/*Append a polyline with nVertices where vertices is an array of Vertex
  to picture pic.  proximity is extra proximity for lines that are to
  go above something.  Returns true iff successful.*/
{
    register PolyPtr item;
    register int k, d;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYLINE;
	item -> item . proximity = proximity;
	item -> item . flags = 0;

	item -> lineWidth = lineWidth;
	item -> nVertices = nVertices;
	for (k = 0; k < nVertices; ++k)
	{
	    item -> vertices[k] = vertices[k];
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
PolyPtr AppendSPolypointToPicture(ObjPtr pic, int proximity, long nVertices, VertexPtr vertices[])
#else
PolyPtr AppendSPolypointToPicture(pic, proximity, nVertices, vertices)
ObjPtr pic;
int lineWidth;
int proximity;
long nVertices;
Vertex *vertices[];
#endif
/*Append a polypoint with nVertices where vertices is an array of VertexPtr
  to picture pic.  proximity is extra proximity for points that are to
  go above something.  Returns true iff successful.*/
{
    PolyPtr item;
    int k;

    item = (PolyPtr)
	   Alloc(sizeof(Polygon) + (nVertices - 1) * sizeof(VertexPtr));
    if (item)
    {
	item -> item . type = POLYPOINT;
	item -> item . proximity = proximity;

	item -> lineWidth = 1;
	item -> nVertices = nVertices;
	for (k = 0; k < nVertices; ++k)
	{
	    item -> vertices[k] = vertices[k];
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
FrustumPtr AppendFrustumToPicture(ObjPtr pic, float end1[3], float rad1,
		float end2[3], float rad2, int colorIndex)
#else
FrustumPtr AppendFrustumToPicture(pic, end1, rad1, end2, rad2, colorIndex)
ObjPtr pic;
float end1[3];
float rad1;
float end2[3];
float rad2;
int colorIndex;
#endif
/*Appends a conical frustum with endpoints end1 and end2 and radii rad1 and
  rad2 to picture pic*/
{
    FrustumPtr item;
    int k;

    item = newp(Frustum);
    if (item)
    {
	item -> item . type = FRUSTUM;
	item -> rad1 = rad1;
	item -> rad2 = rad2;
	item -> colorIndex = colorIndex;
	for (k = 0; k < 3; ++k)
	{
	    item -> end1[k] = end1[k];
	    item -> end2[k] = end2[k];
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

#ifdef HAVE_PROTOTYPES
SpherePtr AppendSphereToPicture(ObjPtr pic, float center[3], float radius, int colorIndex)
#else
SpherePtr AppendSphereToPicture(pic, center, radius, colorIndex)
ObjPtr pic;
float center[3];
float radius;
int colorIndex;
#endif
/*Appends a sphere with center center and radius radius to pic*/
{
    SpherePtr item;
    int k;

    item = newp(Sphere);
    if (item)
    {
	item -> item . type = SPHERE;
	item -> radius = radius;
	item -> centerVertex = NewVertex(pic, 0);
	item -> centerVertex -> colorIndex = colorIndex;
	for (k = 0; k < 3; ++k)
	{
	    item -> centerVertex -> position[k] = center[k];
	}
	AppendItemToPicture((PicPtr) pic, (PicItemPtr) item);
	return item;
    }
    else
    {
	return 0;
    }
}

static float curCenter[3];	/*Current center*/
static float curRadius;		/*Current radius*/
static int curSub;		/*Current number of subdivisions*/
static int curNSides;		/*Current number of sides*/

#ifdef GRAPHICS
void DrawQuadrant(r1, r2, r3, level)
float r1[3];
float r2[3];
float r3[3];
int level;
/*Draws a quadrant of sphere at center curCenter with radius curRadius given 
  radial normal vectors r1, r2, and r3, using current color table, at recursion 
  level level.  Assumes that the appropriate color table is current*/
{
    if (level <= 0)
    {
	/*Maximum number of subdivisions has been reached*/
	float v1[3], v2[3], v3[3];
	v1[0] = curCenter[0] + r1[0] * curRadius;
	v1[1] = curCenter[1] + r1[1] * curRadius;
	v1[2] = curCenter[2] + r1[2] * curRadius;
	v2[0] = curCenter[0] + r2[0] * curRadius;
	v2[1] = curCenter[1] + r2[1] * curRadius;
	v2[2] = curCenter[2] + r2[2] * curRadius;
	v3[0] = curCenter[0] + r3[0] * curRadius;
	v3[1] = curCenter[1] + r3[1] * curRadius;
	v3[2] = curCenter[2] + r3[2] * curRadius;

        if (rgbp)
        {
            bgnpolygon();
	    N3F(r1);
	    v3f(v1);
	    N3F(r2);
	    v3f(v2);
	    N3F(r3);
	    v3f(v3);
	    endpolygon();
        }
	else
        {
	    bgnpolygon();
	    v3f(v1);
	    v3f(v2);
	    v3f(v3);
            endpolygon();
        }
    }
    else
    {
	/*Do another subdivision*/
	float r12[3], r23[3], r31[3];	/*Inner triangle subdivisions*/
	r12[0] = (r1[0] + r2[0]);
	r12[1] = (r1[1] + r2[1]);
	r12[2] = (r1[2] + r2[2]);
	r23[0] = (r2[0] + r3[0]);
	r23[1] = (r2[1] + r3[1]);
	r23[2] = (r2[2] + r3[2]);
	r31[0] = (r3[0] + r1[0]);
	r31[1] = (r3[1] + r1[1]);
	r31[2] = (r3[2] + r1[2]);
	NORMALIZE(r12);
	NORMALIZE(r23);
	NORMALIZE(r31);

	/*Draw the subdivisions*/
	DrawQuadrant(r1, r12, r31, level - 1);
	DrawQuadrant(r12, r2, r23, level - 1);
	DrawQuadrant(r23, r3, r31, level - 1);
	DrawQuadrant(r12, r23, r31, level - 1);
    }
}
#endif

static Bool formsAreEqual;

#ifdef HAVE_PROTOTYPES
void UnColorPicture(ObjPtr pic)
#else
void UnColorPicture(pic)
ObjPtr pic;
#endif
/*Uncolors picture pic*/
{
    PicItemPtr curItem;
    ObjPtr repObj;

    curItem = ((PicPtr) pic) -> items;

    while (curItem)
    {
	curItem = curItem -> next;
    }
}

#ifdef HAVE_PROTOTYPES
void ColorItemByObject(PicItemPtr curItem,ObjPtr colorObj, Bool interpolate)
#else
void ColorItemByObject(curItem, colorObj, interpolate)
PicItemPtr curItem;
ObjPtr colorObj;
Bool interpolate;
#endif
/*Colors item curItem by colorObj*/
{
    long k;
    long nVertices;
    VertexPtr *vertices;

    switch(curItem -> type)
    {
	case POLYGON:
	case POLYLINE:
	case POLYPOINT:
	    nVertices = ((PolyPtr) curItem) -> nVertices;
	    vertices = ((PolyPtr) curItem) -> vertices;
	    break;
	case POLYGONS:
	    ColorItemsByObject((PicItemPtr) ((PolysPtr) curItem) -> polygons, 
		    colorObj, interpolate);
	    return;
	    break;
	case RECTMESH:
	    {
		int i, j;
		real sample;
		int index;
		long offset;
		real position[3];


		vertices = ((RectMeshPtr) curItem) -> vertices;
		if (((RectMeshPtr) curItem) -> inCenter)
		{
		    for (i = 0; i < ((RectMeshPtr) curItem) -> xDim - 1; ++i)
		    {
			for (j = 0; j < ((RectMeshPtr) curItem) -> yDim - 1; ++j)
			{
			    offset = RECTMESHCENTER((RectMeshPtr) curItem, i, j);
			    position[0] = vertices[offset] -> position[0];
			    position[1] = vertices[offset] -> position[1];
			    position[2] = vertices[offset] -> position[2];

			    sample = SampleSpatScalar(FIELD1, FIELD2,
				3, position, interpolate);

			    index = GetRealColorIndex(sample);
			    vertices[offset] -> colorIndex = index;
			}
		    }
		    InterpRectVertices((RectMeshPtr) curItem);
		}
		else
		{
		    for (i = 0; i < ((RectMeshPtr) curItem) -> xDim; ++i)
		    {
			for (j = 0; j < ((RectMeshPtr) curItem) -> yDim; ++j)
			{
			    offset = RECTMESHVERTEX((RectMeshPtr) curItem, i, j);

			    position[0] = vertices[offset] -> position[0];
			    position[1] = vertices[offset] -> position[1];
			    position[2] = vertices[offset] -> position[2];

			    sample = SampleSpatScalar(FIELD1, FIELD2,
				3, position, interpolate);

			    index = GetRealColorIndex(sample);
			    vertices[offset] -> colorIndex = index;
			}
		    }
		    InterpRectCenters((RectMeshPtr) curItem);
		}
	    }
	    return;
	    break;
	default:
	    return;
    }

    if (formsAreEqual && curItem -> flags & PF_CANONVERTICES)
    {
	int nTraversalDims;		/*Number of dimensions to traverse*/
	int whichDim;			/*Dimension counter*/
	long *traversalDims = 0;	/*Dimensions of the dataset to traverse*/
	long *index = 0;		/*Counting index*/
	long nNodes;			/*# Total nodes in the dataset*/
	int nComponents;		/*# components of data form*/
	real sample;

	/*Get the information on traversing the dataset*/
	nTraversalDims = CountTraversalDims(FORMFIELD);
	if (nTraversalDims)
	{
	    traversalDims = (long *) Alloc(sizeof(long) * nTraversalDims);
	    index = (long *) Alloc(sizeof(long) * nTraversalDims);
	}
	else
	{
	    index = (long *) Alloc(sizeof(long));
	}
	GetTraversalDims(FORMFIELD, traversalDims);

	/*Calculate the number of nodes*/
	nNodes = 1;
	for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	{
	    if (traversalDims[whichDim] > 0)
	    {
		nNodes *= traversalDims[whichDim];
	    }
	}

	/*Color as many vertices as we have nodes*/

	/*Zero the index*/
	for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	{
	    index[whichDim] = 0;
	}

	/*Traverse all the points*/
	k = 0;
	do
	{
	    /*Sample the location at a point*/
	    sample = SelectFieldScalar(FIELD1, index);
	    vertices[k] -> colorIndex = GetRealColorIndex(sample);
	    ++k;

	    /*Advance to next point*/
	    for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	    {
		if (traversalDims[whichDim] > 0)
		{
		    if ((++index[whichDim]) >= traversalDims[whichDim])
		    {
			index[whichDim] = 0;
		    }
		    else
		    {
			break;
		    }
		}
	    }
	} while (whichDim < nTraversalDims); /*Break is based on advance*/

	/*Free up temporary storage*/
	SAFEFREE(traversalDims);
	SAFEFREE(index);
    }
    else
    {
	real position[3];
	real sample;
	int index;
	int j;
	/*Make it colored*/
	for (k = 0; k < nVertices; ++k)
	{
    
	    position[0] = vertices[k] -> position[0];
	    position[1] = vertices[k] -> position[1];
	    position[2] = vertices[k] -> position[2];
    
	    sample = SampleSpatScalar(FIELD1, FIELD2,
				3, position, interpolate);
	
	    index = GetRealColorIndex(sample);
	    vertices[k] -> colorIndex = index;
	}
    }
}

#ifdef HAVE_PROTOTYPES
void ColorItemWithIndex(PicItemPtr curItem, int index)
#else
void ColorItemWithIndex(curItem, index)
PicItemPtr curItem;
int index;
#endif
/*Colors item curItem with index*/
{
    long k;
	long nVertices;
	VertexPtr *vertices;

	switch(curItem -> type)
	{
	    case POLYGON:
	    case POLYLINE:
		nVertices = ((PolyPtr) curItem) -> nVertices;
		vertices = ((PolyPtr) curItem) -> vertices;
		break;
	    case POLYGONS:
		ColorItemsWithIndex((PicItemPtr) ((PolysPtr) curItem) -> polygons, 
			index);
		return;
		break;
	    case RECTMESH:
		{
		    int i, j;
		    real sample;
		    long offset;

		    vertices = ((RectMeshPtr) curItem) -> vertices;
		    if (((RectMeshPtr) curItem) -> inCenter)
		    {
			for (i = 0; i < ((RectMeshPtr) curItem) -> xDim - 1; ++i)
			{
			    for (j = 0; j < ((RectMeshPtr) curItem) -> yDim - 1; ++j)
			    {
				offset = RECTMESHCENTER((RectMeshPtr) curItem, i, j);

				vertices[offset] -> colorIndex = index;
			    }
			}
			InterpRectVertices((RectMeshPtr) curItem);
		    }
		    else
		    {
			for (i = 0; i < ((RectMeshPtr) curItem) -> xDim; ++i)
			{
			    for (j = 0; j < ((RectMeshPtr) curItem) -> yDim; ++j)
			    {
				offset = RECTMESHVERTEX((RectMeshPtr) curItem, i, j);

				vertices[offset] -> colorIndex = index;
			    }
			}
			InterpRectCenters((RectMeshPtr) curItem);
		    }
		}
		return;
		break;
	    default:
		return;
	}

	/*Make it colored*/
	for (k = 0; k < nVertices; ++k)
	{
	    vertices[k] -> colorIndex = index;
	}
}

#ifdef HAVE_PROTOTYPES
void ColorItemsByObject(PicItemPtr curItem,ObjPtr colorObj, Bool interpolate)
#else
void ColorItemsByObject(curItem, colorObj, interpolate)
PicItemPtr curItem;
ObjPtr colorObj;
Bool interpolate;
#endif
/*Colors items at curItem by colorObj*/
{
    ObjPtr repObj;
    ObjPtr colors;
    long k;

    MakeVar(colorObj, CPALETTE);
    colors = GetPaletteVar("ColorItemsByObject", colorObj, CPALETTE);
    if (!colors) return;
    SetPalette(colors);

    while (curItem)
    {
	ColorItemByObject(curItem, colorObj, interpolate);

	curItem = curItem -> next;
    }
}

#ifdef HAVE_PROTOTYPES
void ColorItemsWithIndex(PicItemPtr curItem, int index)
#else
void ColorItemsWithIndex(curItem, index)
PicItemPtr curItem;
int index;
#endif
/*Colors items starting at curItem with index*/
{
    ObjPtr repObj;
    ObjPtr colors;
    long k;

    while (curItem)
    {
	ColorItemWithIndex(curItem, index);

	curItem = curItem -> next;
    }
}

#ifdef HAVE_PROTOTYPES
void ColorPictureByObject(ObjPtr pic,ObjPtr colorObj, Bool interpolate)
#else
void ColorPictureByObject(pic, colorObj, interpolate)
ObjPtr pic;
ObjPtr colorObj;
Bool interpolate;
#endif
/*Colors picture pic by colorObj.  if interpolate, interpolates on lookup*/
{
    register VertexPtr runner;
    register VertexPtr last = NULL;
    int nTraversalDims;		/*Number of dimensions to traverse*/
    int whichDim;			/*Dimension counter*/
    long *traversalDims = 0;	/*Dimensions of the dataset to traverse*/
    long *index = 0;		/*Counting index*/
    long nNodes;			/*# Total nodes in the dataset*/
    int nComponents;		/*# components of data form*/
    real sample;
    ObjPtr palette;

    ObjPtr mainDataset = NULLOBJ;

    SetCurField(FIELD1, colorObj);
    SetCurForm(FIELD2, colorObj);

    MakeVar(colorObj, CPALETTE);
    palette = GetPaletteVar("ColorPictureByObject", colorObj, CPALETTE);
    if (palette)
    {
	SetPalette(palette);
    }

    formsAreEqual = false;

    mainDataset = GetVar(pic, MAINDATASET);

    if (mainDataset)
    {
	SetCurForm(FORMFIELD, mainDataset);
	if (IdenticalFields(FORMFIELD, FIELD2))
	{
	    formsAreEqual = true;
	}
    }

    /*Make all vertices not colored*/
    runner = ((PicPtr) pic) -> vertices;
    while (runner)
    {
	runner -> flags &= ~VF_COLORED;
	runner = runner -> next;
    }

    /*Create information for the canonical group*/
    if (formsAreEqual)
    {
	nTraversalDims = CountTraversalDims(FORMFIELD);
	if (nTraversalDims)
	{
	    traversalDims = (long *) Alloc(sizeof(long) * nTraversalDims);
	    index = (long *) Alloc(sizeof(long) * nTraversalDims);
	}
	else
	{
	    index = (long *) Alloc(sizeof(long));
	}
	GetTraversalDims(FORMFIELD, traversalDims);

	/*Calculate the number of nodes*/
	nNodes = 1;
	for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	{
	    if (traversalDims[whichDim] > 0)
	    {
		nNodes *= traversalDims[whichDim];
	    }
	}
    }

    runner = ((PicPtr) pic) -> vertices;
    while (runner)
    {
	if (!(runner -> flags & VF_COLORED))
	{
	    runner -> flags |= VF_COLORED;

	    if (formsAreEqual && runner -> flags & VF_FIRSTCANON)
	    {
		/*Begin a canonical group*/
		
		/*Zero the index*/
		for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
		{
		    index[whichDim] = 0;
		}
	    }

	    if (formsAreEqual && runner -> flags & VF_NEXTCANON)
	    {
		/*Advance to next point*/
		for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
		{
		    if (traversalDims[whichDim] > 0)
		    {
			if ((++index[whichDim]) >= traversalDims[whichDim])
			{
			    index[whichDim] = 0;
			}
			else
			{
			    break;
			}
		    }
		}
		if (whichDim >= nTraversalDims)
		{
		    /*Wraparound*/
		    for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
		    {
			index[whichDim] = 0;
		    }
		}	
	    }

	    if (last && (runner -> flags & VF_SAMEPLACE))
	    {
		runner -> colorIndex = last -> colorIndex;
	    }
	    else if (formsAreEqual &&
		((runner -> flags & VF_NEXTCANON) ||
		 (runner -> flags & VF_FIRSTCANON)))
	    {
		/*Sample from dataform*/
		sample = SelectFieldScalar(FIELD1, index);
		runner -> colorIndex = GetRealColorIndex(sample);
	    }
	    else
	    {
		/*Sample arbitrarily*/
		sample = SampleSpatScalar(FIELD1, FIELD2,
				3, runner -> position, interpolate);
	
		runner -> colorIndex = GetRealColorIndex(sample);
	    }
	}
	last = runner;
	runner = runner -> next;
    }

    if (formsAreEqual)
    {
	/*Free up temporary storage*/
	SAFEFREE(traversalDims);
	SAFEFREE(index);
    }
}

#ifdef HAVE_PROTOTYPES
void PickCanonicalPictureVertices(ObjPtr pic)
#else
void PickCanonicalPictureVertices(pic)
ObjPtr pic;
#endif
/*Picks the vertices in a picture*/
{
    VertexPtr runner;
 
    runner = ((PicPtr) pic) -> vertices;
    while (runner)
    {
	PickVertex(runner);
	bgnpoint();
	v3f(runner -> position);
	endpoint();
	runner = runner -> next;
    }
}

#ifdef HAVE_PROTOTYPES
void DeformPictureByObject(ObjPtr pic)
#else
void DeformPictureByObject(pic)
ObjPtr pic;
#endif
/*Deforms picture pic.

  ASSUMES that SetupDeformation has been done on the visualization object
*/
{
    register VertexPtr runner;

    runner = ((PicPtr) pic) -> vertices;
    while (runner)
    {
	runner -> flags &= ~VF_DEFORMED;
	runner = runner -> next;
    }

    runner = ((PicPtr) pic) -> vertices;
    while (runner)
    {
	if (!(runner -> flags & VF_DEFORMED))
	{
	    runner -> flags |= VF_DEFORMED;
	    DEFORMVERTEX(runner);
	}
	runner = runner -> next;
    }

    CalcPictureNormals(pic);
}

#ifdef HAVE_PROTOTYPES
void CalcPolyNormals(PolyPtr poly)
#else
void CalcPolyNormals(poly)
PolyPtr poly;
#endif
/*Calculates the normals in a polygon*/
{
    float x[3], nml[3], v1[3], v2[3];
    register float mag;
    register int i, p, m, n;

    /*Set up z normal, just in case*/
    nml[0] = 0.0;
    nml[1] = 0.0;
    nml[2] = 1.0;

    /*Go around, starting at 1 for robustness*/
    for (i = 1; i <= poly -> nVertices; ++i)
    {
	p = i - 1;
	m = i % (poly -> nVertices);
	n = (i + 1) % (poly -> nVertices);
	
	v1[0] = poly -> vertices[p] -> position[0] -
		poly -> vertices[m] -> position[0];
	v1[1] = poly -> vertices[p] -> position[1] -
		poly -> vertices[m] -> position[1];
	v1[2] = poly -> vertices[p] -> position[2] -
		poly -> vertices[m] -> position[2];

	v2[0] = poly -> vertices[n] -> position[0] -
		poly -> vertices[m] -> position[0];
	v2[1] = poly -> vertices[n] -> position[1] -
		poly -> vertices[m] -> position[1];
	v2[2] = poly -> vertices[n] -> position[2] -
		poly -> vertices[m] -> position[2];

	CROSS(v2, v1, x);

	mag = MAG3(x);

	if (mag > 0.0)
	{
	    /*Normal stuff*/
	    mag = 1.0 / mag;
	    nml[0] = x[0] * mag;
	    nml[1] = x[1] * mag;
	    nml[2] = x[2] * mag;
	}
	poly -> vertices[m] -> normal[0] += nml[0];
	poly -> vertices[m] -> normal[1] += nml[1];
	poly -> vertices[m] -> normal[2] += nml[2];
    }
}

#ifdef HAVE_PROTOTYPES
void CalcPolytriNormals(PolyPtr poly)
#else
void CalcPolytriNormals(poly)
PolyPtr poly;
#endif
/*Calculates the normals in a triangular mesh strip*/
{
    float x[3], v1[3], v2[3];
    register float mag;
    register int i;

    i = 0;
    while (i < poly -> nVertices - 2)
    {
	/*Do a clockwise panel*/
	v1[0] = poly -> vertices[i + 1] -> position[0] -
		poly -> vertices[i] -> position[0];
	v1[1] = poly -> vertices[i + 1] -> position[1] -
		poly -> vertices[i] -> position[1];
	v1[2] = poly -> vertices[i + 1] -> position[2] -
		poly -> vertices[i] -> position[2];

	v2[0] = poly -> vertices[i + 2] -> position[0] -
		poly -> vertices[i] -> position[0];
	v2[1] = poly -> vertices[i + 2] -> position[1] -
		poly -> vertices[i] -> position[1];
	v2[2] = poly -> vertices[i + 2] -> position[2] -
		poly -> vertices[i] -> position[2];

	CROSS(v1, v2, x);

	mag = MAG3(x);
	if (mag > 0.0)
	{
	    /*Normal stuff*/
	    mag = 1.0 / mag;
	    poly -> vertices[i] -> normal[0] += x[0] * mag;
	    poly -> vertices[i] -> normal[1] += x[1] * mag;
	    poly -> vertices[i] -> normal[2] += x[2] * mag;

	    poly -> vertices[i + 1] -> normal[0] += x[0] * mag;
	    poly -> vertices[i + 1] -> normal[1] += x[1] * mag;
	    poly -> vertices[i + 1] -> normal[2] += x[2] * mag;

	    poly -> vertices[i + 2] -> normal[0] += x[0] * mag;
	    poly -> vertices[i + 2] -> normal[1] += x[1] * mag;
	    poly -> vertices[i + 2] -> normal[2] += x[2] * mag;
	}

	++i;

	if (i < poly -> nVertices - 2)
	{
	     /*Do a counterclockwise panel*/
	    v1[0] = poly -> vertices[i + 2] -> position[0] -
		    poly -> vertices[i] -> position[0];
	    v1[1] = poly -> vertices[i + 2] -> position[1] -
		    poly -> vertices[i] -> position[1];
	    v1[2] = poly -> vertices[i + 2] -> position[2] -
		    poly -> vertices[i] -> position[2];
    
	    v2[0] = poly -> vertices[i + 1] -> position[0] -
		    poly -> vertices[i] -> position[0];
	    v2[1] = poly -> vertices[i + 1] -> position[1] -
		    poly -> vertices[i] -> position[1];
	    v2[2] = poly -> vertices[i + 1] -> position[2] -
		    poly -> vertices[i] -> position[2];
    
	    CROSS(v1, v2, x);
	    mag = MAG3(x);
	    if (mag > 0.0)
	    {
		/*Normal stuff*/
		mag = 1.0 / mag;
		poly -> vertices[i] -> normal[0] += x[0] * mag;
		poly -> vertices[i] -> normal[1] += x[1] * mag;
		poly -> vertices[i] -> normal[2] += x[2] * mag;
    
		poly -> vertices[i + 1] -> normal[0] += x[0] * mag;
		poly -> vertices[i + 1] -> normal[1] += x[1] * mag;
		poly -> vertices[i + 1] -> normal[2] += x[2] * mag;
    
		poly -> vertices[i + 2] -> normal[0] += x[0] * mag;
		poly -> vertices[i + 2] -> normal[1] += x[1] * mag;
		poly -> vertices[i + 2] -> normal[2] += x[2] * mag;
	    }
    
	    ++i;
	}
    }
}

#ifdef HAVE_PROTOTYPES
void CalcPictureNormals(ObjPtr pic)
#else
void CalcPictureNormals(pic)
ObjPtr pic;
#endif
/*Calculates the normals within pic*/
{
    PicItemPtr curItem;
    VertexPtr vertices;

    /*Go through and zero the normals*/
    vertices = ((PicPtr) pic) -> vertices;
    while (vertices)
    {
	vertices -> normal[0] = 0.0;
	vertices -> normal[1] = 0.0;
	vertices -> normal[2] = 0.0;
	vertices = vertices -> next;
    }

    curItem = ((PicPtr) pic) -> items;
    while (curItem)
    {
	switch(curItem -> type)
	{
	    case POLYGON:
	  	CalcPolyNormals((PolyPtr) curItem);
		break;
	    case POLYTRI:
		CalcPolytriNormals((PolyPtr) curItem);
		break;
	    case POLYGONS:
		{
		    PolyPtr runner;
		    runner = ((PolysPtr) ((PicPtr) pic) -> items) -> polygons;
		    while (runner)
		    {
			CalcPolyNormals(runner);
			runner = (PolyPtr) runner -> item . next;
		    }
		}
		break;
	    case RECTMESH:
		CalcRectNormals((RectMeshPtr) curItem);
		break;
	}
	curItem = curItem -> next;
    }

    /*Go through and NORMALIZE the normals*/
    vertices = ((PicPtr) pic) -> vertices;
    while (vertices)
    {
	double f;
	f = MAG3(vertices -> normal);
	if (f > 0.0)
	{
	    f = 1.0 / f;
	    vertices -> normal[0] *= f;
	    vertices -> normal[1] *= f;
	    vertices -> normal[2] *= f;
	}
	else
	{
	    vertices -> normal[2] = 1.0;
	}
	vertices = vertices -> next;
    }

}

#ifdef GRAPHICS
/*Shading models*/
int shadeModels[2][4][3] =
    {
	{{    FLAT,    FLAT, GOURAUD},
	 {    FLAT,    FLAT, GOURAUD},
	 { GOURAUD,    FLAT, GOURAUD},
	 { GOURAUD, GOURAUD, GOURAUD}},
	{{    FLAT,    FLAT, GOURAUD},
	 {    FLAT,    FLAT, GOURAUD},
	 { GOURAUD, GOURAUD, GOURAUD},
	 { GOURAUD, GOURAUD, GOURAUD}}
    };

#define PrepareToDrawC						\
	color(curBeg + curNColors - 4);
	
#define PrepareToDrawRGB						\
	C3S(curColors[curNColors - 4]);

#define PrepareToDrawCDepthCue					\
	lshaderange(curBeg + 2, curBeg + curNColors - 4, curZMin, curZMax);

#define PrepareToDrawRGBDepthCue					\
	lRGBrange(0,		\
		  0,		\
		  0,		\
		  curColors[curNColors - 4][0],		\
		  curColors[curNColors - 4][1],		\
		  curColors[curNColors - 4][2], curZMin, curZMax);


float tN[3];

/*Traversal macros to make life a little easier*/

/*Polygon traversal macro.  Decomposes simply into triangles*/
#define TRAVERSEPOLY {							\
    register VertexPtr *v1, *v2;					\
    v1 = ((PolyPtr) curItem) -> vertices;				\
	    v2 = ((PolyPtr) curItem) -> vertices + 			\
	    (((PolyPtr) curItem) -> nVertices - 1);			\
    bgntmesh();								\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
									\
    while (v2 >= v1)							\
    {									\
	/*Emit v1 and increment*/					\
	V(v1);								\
	++v1;								\
	if (v2 >= v1)							\
	{								\
	    /*Emit v2 and decrement*/					\
	    V(v2);							\
	    --v2;							\
	}								\
    }									\
    endtmesh();}

/*Polytri traversal macro.  */
#define TRAVERSEPOLYTRI {						\
    register int k, n;							\
    register VertexPtr *v1;						\
    n = ((PolyPtr) curItem) -> nVertices;				\
    v1 = ((PolyPtr) curItem) -> vertices;				\
    bgntmesh();								\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
									\
    for (k = 0; k < n; ++k)						\
    {									\
	/*Emit v1 and increment*/					\
	V(v1); ++v1;							\
    }									\
    endtmesh();}

/*Planar traversal macro*/
#define TRAVERSEPLANARPOLY {						\
    register VertexPtr *v1;						\
    register int k;							\
    v1 = ((PolyPtr) curItem) -> vertices;				\
									\
    bgnpolygon();							\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
    for (k = 0; k < ((PolyPtr) curItem) -> nVertices; ++k)		\
    {									\
	/*Emit v1 and increment*/					\
	V(v1);								\
	++v1;								\
    }									\
    endpolygon();}

/*Polyline traversal macro*/
#define TRAVERSEPOLYLINE {						\
    register VertexPtr *v1;						\
    register int k;							\
    v1 = ((PolyPtr) curItem) -> vertices;				\
									\
    bgnline();								\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
    for (k = 0; k < ((PolyPtr) curItem) -> nVertices; ++k)		\
    {									\
	/*Emit v1 and increment*/					\
	V(v1);								\
	++v1;								\
    }									\
    endline();}

/*Polypoint traversal macro*/
#define TRAVERSEPOLYPOINT {						\
    register VertexPtr *v1;						\
    register int k;							\
    v1 = ((PolyPtr) curItem) -> vertices;				\
									\
    bgnpoint();								\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
    for (k = 0; k < ((PolyPtr) curItem) -> nVertices; ++k)		\
    {									\
	/*Emit v1 and increment*/					\
	if (0 == ((*v1) -> flags & VF_DONTDRAW)) {V(v1)}			\
	++v1;								\
    }									\
    endpoint();								\
}

/*Closed polyline traversal macro*/
#define TRAVERSECLOSEDPOLYLINE {						\
    register VertexPtr *v1;						\
    register int k;							\
    v1 = ((PolyPtr) curItem) -> vertices;				\
									\
    bgnclosedline();								\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
    for (k = 0; k < ((PolyPtr) curItem) -> nVertices; ++k)		\
    {									\
	/*Emit v1 and increment*/					\
	V(v1);								\
	++v1;								\
    }									\
    endclosedline();}

/*Closed polytri line traversal macro*/
#define TRAVERSECLOSEDPOLYTRI {						\
    register VertexPtr *v1;						\
    register int k;							\
    v1 = ((PolyPtr) curItem) -> vertices;				\
									\
    bgnclosedline();							\
    P(v1, ((PolyPtr) curItem) -> nVertices);				\
    V(v1);								\
    k = 1; ++v1;							\
    while (k < ((PolyPtr) curItem) -> nVertices)			\
    {									\
	V(v1);								\
	v1 += 2;							\
	k += 2;								\
    } --k; --v1;							\
    if (k < ((PolyPtr) curItem) -> nVertices) {k += 2; v1 += 2;}	\
    do									\
    {									\
	k -= 2;								\
	v1 -= 2;							\
	V(v1);								\
    } while (k > 0);							\
    endclosedline();}

/*Dots traversal macro*/
#define TRAVERSEDOTS	{						\
    VertexPtr vertices;							\
    vertices = ((PicPtr) pic) -> vertices;				\
    bgnpoint();								\
    P(&vertices, 1);							\
    while (vertices)							\
    {									\
	if (0 == (vertices -> flags & VF_DONTDRAW)) {V(&vertices)}	\
	vertices = vertices -> next;					\
    }									\
    endpoint();}

/*Wire frame rectangle traversal macro*/
#define TRAVERSEWRECTMESH						\
    {									\
    register int i, j;							\
    int xDim, yDim;							\
    register VertexPtr *vertexPtr;					\
    int nX, nY;								\
									\
    vertexPtr = ((RectMeshPtr) curItem) -> vertices;			\
    xDim = ((RectMeshPtr) curItem) -> xDim;				\
    yDim = ((RectMeshPtr) curItem) -> yDim;				\
    nX = (2 * yDim - 1);						\
    nY = 2;								\
									\
    /*Draw the fast moving axes*/					\
    for (i = 0; i < xDim; ++i)						\
    {									\
	vertexPtr = ((RectMeshPtr) curItem) -> vertices +		\
		RECTMESHVERTEX(((RectMeshPtr) curItem), i, 0);		\
	bgnline();							\
	P(vertexPtr, 1);						\
	for (j = 0; j < yDim; ++j)					\
	{								\
	    V(vertexPtr);						\
	    vertexPtr += nY;						\
	}								\
	endline();							\
    }									\
									\
    /*Draw the slow moving axes*/					\
    for (j = 0; j < yDim; ++j)						\
    {									\
	vertexPtr = ((RectMeshPtr) curItem) -> vertices +		\
			RECTMESHVERTEX(((RectMeshPtr) curItem), 0, j);	\
	bgnline();							\
	P(vertexPtr, 1);						\
	for (i = 0; i < xDim; ++i)					\
	{								\
	    V(vertexPtr);						\
	    vertexPtr += nX;						\
	}								\
	endline();							\
    }									\
    }

/*Wire frame monocolor rectangle traversal macro*/
#define TRAVERSEWMONOCOLORRECTMESH						\
    {									\
    register int i, j;							\
    int xDim, yDim;							\
    register VertexPtr *vertexPtr;					\
    int nX, nY;								\
									\
    vertexPtr = ((RectMeshPtr) curItem) -> vertices;			\
    xDim = ((RectMeshPtr) curItem) -> xDim;				\
    yDim = ((RectMeshPtr) curItem) -> yDim;				\
    nX = (2 * yDim - 1);						\
    nY = 2;								\
									\
    /*Draw the fast moving axes*/					\
    for (i = 0; i < xDim; ++i)						\
    {									\
	vertexPtr = ((RectMeshPtr) curItem) -> vertices +		\
		RECTMESHVERTEX(((RectMeshPtr) curItem), i, 0);		\
	for (j = 0; j < yDim - 1; ++j)					\
	{								\
	    bgnline();							\
	    P1(vertexPtr, 1);						\
	    V(vertexPtr);						\
	    vertexPtr += nY;						\
	    V(vertexPtr);						\
	    endline();							\
	}								\
    }									\
									\
    /*Draw the slow moving axes*/					\
    for (j = 0; j < yDim; ++j)						\
    {									\
	vertexPtr = ((RectMeshPtr) curItem) -> vertices +		\
			RECTMESHVERTEX(((RectMeshPtr) curItem), 0, j);	\
	for (i = 0; i < xDim - 1; ++i)					\
	{								\
	    bgnline();							\
	    P2(vertexPtr, 1);						\
	    V(vertexPtr);						\
	    vertexPtr += nX;						\
	    V(vertexPtr);						\
	    endline();							\
	}								\
    }									\
    }


/*Rectangular mesh traversal macro*/
#define TRAVERSERECTMESH						\
    {									\
	register int i, j;						\
	int xDim, yDim;							\
	register VertexPtr *vertexPtr;					\
	int nX, nY;							\
									\
	vertexPtr = ((RectMeshPtr) curItem) -> vertices;		\
	xDim = ((RectMeshPtr) curItem) -> xDim;				\
	yDim = ((RectMeshPtr) curItem) -> yDim;				\
	nY = 2;								\
	nX = (yDim * 2 - 1);						\
	for (i = 0; i < xDim - 1; ++i)					\
	{								\
	    bgntmesh();						\
	    P(vertexPtr, 1);					\
	    V(vertexPtr);					\
	    vertexPtr += nX;					\
	    V(vertexPtr);					\
	    for (j = 1; j < yDim; ++j)				\
	    {							\
		vertexPtr += nY - nX;				\
		V(vertexPtr);					\
		vertexPtr += nX;				\
		V(vertexPtr);					\
	    }							\
	    vertexPtr -= nX - 1;				\
	    endtmesh();						\
	}								\
    }

/*Monocolor Rectangular mesh traversal macro*/
#define TRAVERSEMCRECTMESH						\
    {									\
	register int i, j;						\
	int xDim, yDim;							\
	register VertexPtr *vertexPtr;					\
	int nX, nY;							\
									\
	vertexPtr = ((RectMeshPtr) curItem) -> vertices;		\
	xDim = ((RectMeshPtr) curItem) -> xDim;				\
	yDim = ((RectMeshPtr) curItem) -> yDim;				\
	nY = 2;								\
	nX = (yDim * 2 - 1);						\
	for (i = 0; i < xDim - 1; ++i)					\
	{								\
	    for (j = 0; j < yDim - 1; ++j)				\
	    {								\
		bgntmesh();						\
		P(vertexPtr + 1);						\
		V(vertexPtr);						\
		vertexPtr += nX;					\
		V(vertexPtr);						\
		vertexPtr += nY - nX;					\
		V(vertexPtr);						\
		vertexPtr += nX;					\
		V(vertexPtr);						\
		endtmesh();						\
		vertexPtr -= nX;					\
	    }								\
	    vertexPtr += nY - 1;  /*-1 for missing center*/		\
	}								\
    }
#endif

static void DrawCPolygon(curItem)
PicItemPtr curItem;
/*Draws a color map shaded polygon*/
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPLANARPOLY
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawWCPolygon(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSECLOSEDPOLYLINE
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawCPolytri(curItem)
PicItemPtr curItem;
/*Draws a color map shaded polygon*/
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPOLYTRI
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawWCPolytri(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSECLOSEDPOLYTRI
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawCPolyline(curItem)
PicItemPtr curItem;
/*Draws a color map shaded polyline*/
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPOLYLINE
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawCPolypoint(curItem)
PicItemPtr curItem;
/*Draws a color map shaded polyline*/
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPOLYPOINT
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawRGBPolygon(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPLANARPOLY
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}

static void DrawRGBPolytri(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPOLYTRI
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}

static void DrawWRGBPolygon(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSECLOSEDPOLYLINE
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}

static void DrawWRGBPolytri(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSECLOSEDPOLYTRI
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}

static void DrawRGBPolyline(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPOLYLINE
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}

static void DrawRGBPolypoint(curItem)
PicItemPtr curItem;
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPOLYPOINT
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}
static void DrawCPPolygon(curItem)
PicItemPtr curItem;
/*Draws a color map shaded planarpolygon*/
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPLANARPOLY
#include "ScianStdCDraw.h"
#undef TRAVERSE
#endif
}

static void DrawRGBPPolygon(curItem)
PicItemPtr curItem;
/*Draws an RGB shaded planar polygon*/
{
#ifdef GRAPHICS
#define TRAVERSE TRAVERSEPLANARPOLY
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
#endif
}


static void DrawWCRectMesh(curItem)
PicItemPtr curItem;
/*Draws a color map wire frame rect mesh*/
{
#ifdef GRAPHICS
	if (curLightShading == DEPTHCUELIGHT)
	{
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PCDN
#define V VCDN
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P1 PCDM
#define P2 PCDMSKIP
#define V VCDM
			TRAVERSEWMONOCOLORRECTMESH;
#undef P1
#undef P2
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PCDS
#define V VCDS
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		}
	}
	else
	{
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PCNN
#define V VCNN
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P1 PCNM
#define P2 PCNMSKIP
#define V VCNM
			TRAVERSEWMONOCOLORRECTMESH;
#undef P1
#undef P2
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PCNS
#define V VCNS
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		}
	}
#endif
}

static void DrawWRGBRectMesh(curItem)
PicItemPtr curItem;
/*Draws an RGB wire frame rectangular mesh*/
{
#ifdef GRAPHICS
	if (curLightShading == DEPTHCUELIGHT)
	{
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PRDN
#define V VRDN
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P1 PRDM
#define P2 PRDMSKIP
#define V VRDM
			TRAVERSEWMONOCOLORRECTMESH;
#undef P1
#undef P2
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PRDS
#define V VRDS
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		}
	}
	else
	{
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PRNN
#define V VRNN
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P1 PRNM
#define P2 PRNMSKIP
#define V VRNM
			TRAVERSEWMONOCOLORRECTMESH;
#undef P1
#undef P2
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PRNS
#define V VRNS
			TRAVERSEWRECTMESH;
#undef P
#undef V
			break;
		}
	}
#endif
}

static void DrawCRectMesh(curItem)
PicItemPtr curItem;
/*Draws a rectangular mesh in color map mode*/
{
#ifdef GRAPHICS
	switch (curLightShading)
	{
	    case NOLIGHT:
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PCNN
#define V VCNN
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P MCRPCNM
#define V VCNM
			TRAVERSEMCRECTMESH;
#undef P
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PCNS
#define V VCNS
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		}
		break;
	    case MONOLIGHT:
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PCMN
#define V VCMN
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P MCRPCNM
#define V VCMM
			TRAVERSEMCRECTMESH;
#undef P
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PCMS
#define V VCMS
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		}
		break;
	    case SMOOTHLIGHT:
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PCSN
#define V VCSN
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P MCRPCNM
#define V VCSM
			TRAVERSEMCRECTMESH;
#undef P
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PCSS
#define V VCSS
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		}
		break;
	}
#endif
}

int yaga = 0;

static void DrawRGBRectMesh(curItem)
PicItemPtr curItem;
/*Draws a rectangular mesh in RGB mode*/
{
#ifdef GRAPHICS
	switch (curLightShading)
	{
	    case NOLIGHT:
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PRNN
#define V VRNN
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P MCRPRNM
#define V VRNM
			TRAVERSEMCRECTMESH;
#undef P
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PRNS
#define V VRNS
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		}
		break;
	    case MONOLIGHT:
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PRMN
#define V VRMN
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P MCRPRNM
#define V VRMM
			TRAVERSEMCRECTMESH;
#undef P
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PRMS
#define V VRMS
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		}
		break;
	    case SMOOTHLIGHT:
		switch (curColorShading)
		{
		    case NOCOLOR:
#define P PRSN
#define V VRSN
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		    case MONOCOLOR:
#define P MCRPRNM
#define V VRSM
			TRAVERSEMCRECTMESH;
#undef P
#undef V
			break;
		    case SMOOTHCOLOR:
#define P PRSS
#define V VRSS
			TRAVERSERECTMESH;
#undef P
#undef V
			break;
		}
		break;
	}
#endif
}

void DrawCSphere(curItem)
PicItemPtr curItem;
/*Draws a sphere in cmap mode*/
{
#ifdef GRAPHICS
			{
			    Sphere curSphere;
			    float vx[3], vy[3], vz[3]; /*Vertices stuck out on those axes*/
			    
			    curSphere = *((SpherePtr) curItem);
	
	        	    if (!curIsTransparent)
	        	    {
	       			backface(TRUE);
	        	    }
			    /*Draw the eight quadrants of the sphere*/
			    /* +x +y +z */
			    vx[0] = 1.0;
			    vx[1] = 0.0;
			    vx[2] = 0.0;
			    vy[0] = 0.0;
			    vy[1] = 1.0;
			    vy[2] = 0.0;
			    vz[0] = 0.0;
			    vz[1] = 0.0;
			    vz[2] = 1.0;
	
			    curRadius = curSphere. radius;
			    curCenter[0] = curSphere . centerVertex -> position[0];
			    curCenter[1] = curSphere . centerVertex -> position[1];
			    curCenter[2] = curSphere . centerVertex -> position[2];
			    DrawQuadrant(vx, vy, vz, SPHERESUB);
	
			    /* +y -x +z*/
			    vx[0] = -1.0;
			    DrawQuadrant(vy, vx, vz, SPHERESUB);
	
			    /* -x -y -z*/
			    vy[1] = -1.0;
			    DrawQuadrant(vx, vy, vz, SPHERESUB);
	
			    /* -y +x +z*/
			    vx[0] = 1.0;
			    DrawQuadrant(vy, vx, vz, SPHERESUB);
	
			    /* +x -y -z*/
			    vz[2] = -1.0;
			    DrawQuadrant(vx, vy, vz, SPHERESUB);
	
			    /* +y +x -z*/
			    vy[1] = 1.0;
			    DrawQuadrant(vy, vx, vz, SPHERESUB);
	
			    /* -x +y -z*/
			    vx[0] = -1.0;
			    DrawQuadrant(vx, vy, vz, SPHERESUB);
	
			    /* -y, -x, -z*/
			    vy[1] = -1.0;
			    DrawQuadrant(vy, vx, vz, SPHERESUB);
	
	        	    if (!curIsTransparent)
	        	    {
	       			backface(FALSE);
	        	    }
			}
#endif
}

void DrawSFrustum(curItem)
PicItemPtr curItem;
/*Draws a skeletal frustum*/
{
#ifdef GRAPHICS
    bgnline();
    v3f(((FrustumPtr) curItem) -> end1);
    v3f(((FrustumPtr) curItem) -> end2);
    endline();
#endif
}

void DrawCFrustum(curItem)
PicItemPtr curItem;
/*Draws a color map frustum*/
{
#ifdef GRAPHICS
    Frustum curFrustum;
    float a[3];		/*Axial unit vector*/
    float r[CYLSIDES][3];
    double length;	/*Length of cylinder*/
    double nf;		/*Normalization factor*/
			/*Radial units around cylinder*/
    int end;		/*Counter for which end*/
    int b;		/*Counter around radius*/
    int d;		/*Delta for subdivision*/
    int k;		/*Random counter*/

    if (!curIsTransparent)
    {
	backface(TRUE);
    }

    /*Copy the frustum into curFrustum for speed*/
    curFrustum = *((FrustumPtr) curItem);

    /*First make an axial unit vector*/
    a[0] = curFrustum . end2[0] -
	   curFrustum . end1[0];
    a[1] = curFrustum . end2[1] -
	   curFrustum . end1[1];
    a[2] = curFrustum . end2[2] -
	   curFrustum . end1[2];
    length = sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
    nf = 1.0 / length;
    a[0] *= nf; a[1] *= nf; a[2] *= nf;

    /*See if it's nearly colinear with i*/
    if (ABS(a[0]) > 0.8)
    {
	/*It is, so cross by j to get first r*/
	r[0][0] = -a[2];
	r[0][1] = 0.0;
	r[0][2] = a[0];
    }
    else
    {
	/*It isn't, so cross by i to get first r*/
	r[0][0] = 0.0;
	r[0][1] = a[2];
	r[0][2] = -a[1];
    }
    
    /*Normalize r*/
    length = sqrt(r[0][0]*r[0][0] + r[0][1]*r[0][1] + r[0][2]*r[0][2]);
    nf = 1.0 / length;
    r[0][0] *= nf; r[0][1] *= nf, r[0][2] *= nf;

    /*Cross a with first radial unit to get orthogonal unit*/
    CROSS(a, r[0], r[CYLSIDES / 4]);

    /*Fill out point radii 3 and 4 by reversing 1 and 2*/
    r[CYLSIDES / 2][0] = -r[0][0];
    r[CYLSIDES / 2][1] = -r[0][1];
    r[CYLSIDES / 2][2] = -r[0][2];

    r[CYLSIDES / 4 * 3][0] = -r[CYLSIDES / 4][0];
    r[CYLSIDES / 4 * 3][1] = -r[CYLSIDES / 4][1];
    r[CYLSIDES / 4 * 3][2] = -r[CYLSIDES / 4][2];

    /*Subdivide the sides of the cylinder*/
    k = 0;
    for (d = CYLSIDES / 4; d > 1; d /= 2)
    {
	register double cef_2;	/*Chord extension factor/2*/
	
	cef_2 = cefs_2[k];
	++k;
	
	for (b = 0; b < CYLSIDES; b += d)
	{
	    register int m, e;	/*Midpoint and endpoint*/

	    /*Interpolate center of chord*/
	    e = (b + d) & (CYLSIDES - 1);	/*Clip circular*/
	    m = b + d / 2;
	    r[m][0] = (r[b][0] + r[e][0]) * cef_2;
	    r[m][1] = (r[b][1] + r[e][1]) * cef_2;
	    r[m][2] = (r[b][2] + r[e][2]) * cef_2;
	}
    }

    /*Draw the cylinder*/
    
#ifndef PSYCHEDELIC
    if (rgbp == false)
    {
	float v[3];
	
	bgntmesh();
	SetRealColor(r[CYLSIDES - 1][0]);
	v[0] = curFrustum . rad2 * r[CYLSIDES - 1][0] + 
	       curFrustum . end2[0];
	v[1] = curFrustum . rad2 * r[CYLSIDES - 1][1] + 
	       curFrustum . end2[1];
	v[2] = curFrustum . rad2 * r[CYLSIDES - 1][2] + 
	       curFrustum . end2[2];
	v3f(v);
	v[0] = curFrustum . rad1 * r[CYLSIDES - 1][0] + 
	       curFrustum . end1[0];
	v[1] = curFrustum . rad1 * r[CYLSIDES - 1][1] + 
	       curFrustum . end1[1];
	v[2] = curFrustum . rad1 * r[CYLSIDES - 1][2] + 
	       curFrustum . end1[2];
	v3f(v);

	for (k = 0; k < CYLSIDES; ++k)
	{
	    SetRealColor(r[k][0]);
	    v[0] = curFrustum . rad2 * r[k][0] + 
		   curFrustum . end2[0];
	    v[1] = curFrustum . rad2 * r[k][1] + 
		   curFrustum . end2[1];
	    v[2] = curFrustum . rad2 * r[k][2] + 
		   curFrustum . end2[2];
	    v3f(v);
	    v[0] = curFrustum . rad1 * r[k][0] + 
		   curFrustum . end1[0];
	    v[1] = curFrustum . rad1 * r[k][1] + 
		   curFrustum . end1[1];
	    v[2] = curFrustum . rad1 * r[k][2] + 
		   curFrustum . end1[2];
	    v3f(v);
	}
	endtmesh();

	/*Do top cap*/
	SetRealColor(a[0]);
	bgnpolygon();
	for (k = 0; k < CYLSIDES; ++k)
	{
	    v[0] = curFrustum . rad2 * r[k][0] + 
		   curFrustum . end2[0];
	    v[1] = curFrustum . rad2 * r[k][1] + 
		   curFrustum . end2[1];
	    v[2] = curFrustum . rad2 * r[k][2] + 
		   curFrustum . end2[2];
	    v3f(v);
	}
	endpolygon();

	/*Do bottom cap*/
	SetRealColor(-a[0]);
	bgnpolygon();
	for (k = CYLSIDES - 1; k >= 0; --k)
	{
	    v[0] = curFrustum . rad1 * r[k][0] + 
		   curFrustum . end1[0];
	    v[1] = curFrustum . rad1 * r[k][1] + 
		   curFrustum . end1[1];
	    v[2] = curFrustum . rad1 * r[k][2] + 
		   curFrustum . end1[2];
	    v3f(v);
	}
	endpolygon();
    }
    else
#endif
    {
	float v[3];

	bgntmesh();
	N3F(r[CYLSIDES - 1]);
	v[0] = curFrustum . rad2 * r[CYLSIDES - 1][0] + 
	       curFrustum . end2[0];
	v[1] = curFrustum . rad2 * r[CYLSIDES - 1][1] + 
	       curFrustum . end2[1];
	v[2] = curFrustum . rad2 * r[CYLSIDES - 1][2] + 
	       curFrustum . end2[2];
	v3f(v);
	v[0] = curFrustum . rad1 * r[CYLSIDES - 1][0] + 
	       curFrustum . end1[0];
	v[1] = curFrustum . rad1 * r[CYLSIDES - 1][1] + 
	       curFrustum . end1[1];
	v[2] = curFrustum . rad1 * r[CYLSIDES - 1][2] + 
	       curFrustum . end1[2];
	v3f(v);

	for (k = 0; k < CYLSIDES; ++k)
	{
	    N3F(r[k]);
	    v[0] = curFrustum . rad2 * r[k][0] + 
		   curFrustum . end2[0];
	    v[1] = curFrustum . rad2 * r[k][1] + 
		   curFrustum . end2[1];
	    v[2] = curFrustum . rad2 * r[k][2] + 
		   curFrustum . end2[2];
	    v3f(v);
	    v[0] = curFrustum . rad1 * r[k][0] + 
		   curFrustum . end1[0];
	    v[1] = curFrustum . rad1 * r[k][1] + 
		   curFrustum . end1[1];
	    v[2] = curFrustum . rad1 * r[k][2] + 
		   curFrustum . end1[2];
	    v3f(v);
	}
	endtmesh();

	/*Do top cap*/
	N3F(a);
	bgnpolygon();
	for (k = 0; k < CYLSIDES; ++k)
	{
	    v[0] = curFrustum . rad2 * r[k][0] + 
		   curFrustum . end2[0];
	    v[1] = curFrustum . rad2 * r[k][1] + 
		   curFrustum . end2[1];
	    v[2] = curFrustum . rad2 * r[k][2] + 
		   curFrustum . end2[2];
	    v3f(v);
	}
	endpolygon();

	/*Do bottom cap*/
	a[0] = -a[0];
	a[1] = -a[1];
	a[2] = -a[2];
	N3F(a);
	bgnpolygon();
	for (k = CYLSIDES - 1; k >= 0; --k)
	{
	    v[0] = curFrustum . rad1 * r[k][0] + 
		   curFrustum . end1[0];
	    v[1] = curFrustum . rad1 * r[k][1] + 
		   curFrustum . end1[1];
	    v[2] = curFrustum . rad1 * r[k][2] + 
		   curFrustum . end1[2];
	    v3f(v);
	}
	endpolygon();
    }
    if (!curIsTransparent)
    {
	backface(FALSE);
    }
#endif
}

void DrawCPolygons(item)
PicItemPtr item;
/*Draws a bunch of polygons in cmode*/
{
#ifdef GRAPHICS
    PolyPtr curItem;

    curItem = ((PolysPtr) item) -> polygons;
    while (curItem)
    {
	DrawCPolygon(curItem);
	curItem = (PolyPtr) curItem -> item . next;
    }
#endif
}

void DrawRGBPolygons(item)
PicItemPtr item;
/*Draws a bunch of polygons in RGB mode*/
{
#ifdef GRAPHICS
    PolyPtr curItem;

    if (!curIsTransparent && ((PolysPtr) item) -> enclosed && !orderReversed)	
    {
	backface(TRUE);
    }
    curItem = ((PolysPtr) item) -> polygons;
    while (curItem)
    {
	DrawRGBPolygon(curItem);
	curItem = (PolyPtr) curItem -> item . next;
    }
    if (!curIsTransparent && ((PolysPtr) item) -> enclosed && !orderReversed)	
    {
	backface(FALSE);
    }
#endif
}

void DrawWCPolygons(item)
PicItemPtr item;
/*Draws a bunch of polygons as wire frames*/
{
#ifdef GRAPHICS
    PolyPtr curItem;
    curItem = ((PolysPtr) item) -> polygons;
    while (curItem)
    {
	DrawWCPolygon(curItem);
	curItem = (PolyPtr) curItem -> item . next;
    }
#endif
}

void DrawWRGBPolygons(item)
PicItemPtr item;
/*Draws a bunch of polygons as wire frames*/
{
#ifdef GRAPHICS
    PolyPtr curItem;
    curItem = ((PolysPtr) item) -> polygons;
    while (curItem)
    {
	DrawWRGBPolygon(curItem);
	curItem = (PolyPtr) curItem -> item . next;
    }
#endif
}

#ifdef GRAPHICS
typedef void (* DrawFunc)();

/*Drawing Table*/

DrawFunc drawFuncs[NPICITEMTYPES][DQ_FULL + 1][2] =
    {
    /*    Wireframe C     Wireframe RGB       Full C          Full RGB           */
	{{DrawWCPolygon,  DrawWRGBPolygon }, {DrawCPolygon,   DrawRGBPolygon }},
	{{DrawSFrustum,   DrawSFrustum    }, {DrawCFrustum,   DrawCFrustum   }},
	{{DrawCSphere,    DrawCSphere     }, {DrawCSphere,    DrawCSphere    }},
	{{DrawWCRectMesh, DrawWRGBRectMesh}, {DrawCRectMesh,  DrawRGBRectMesh}},
	{{DrawWCPolygons, DrawWRGBPolygons}, {DrawCPolygons,  DrawRGBPolygons}},
	{{DrawCPolyline,  DrawRGBPolyline }, {DrawCPolyline,  DrawRGBPolyline}},
	{{DrawCPolypoint, DrawRGBPolypoint}, {DrawCPolypoint, DrawRGBPolypoint}},
	{{DrawWCPolytri,  DrawWRGBPolytri }, {DrawCPolytri,   DrawRGBPolytri }}
    };

#endif

#ifdef HAVE_PROTOTYPES
void PrepareToDraw(Bool isTransparent, int lightShading, int colorShading, ObjPtr colors)
#else
void PrepareToDraw(isTransparent, lightShading, colorShading, colors)
Bool isTransparent;
int lightShading;
int colorShading;
ObjPtr colors;
#endif
/*Prepares to do some drawing*/
{
    /*Store light and color shading*/
    curLightShading = lightShading;
    curColorShading = colorShading;

    if ((drawingQuality != DQ_FULL && curLightShading != DEPTHCUELIGHT)
	|| overrideColor)
    {
	curLightShading = NOLIGHT;
    }

    /*And the transparency*/
    curIsTransparent = isTransparent;
    SetPalette(colors);

    /*Set the color mode*/
    if (rgbp == false)
    {
	curColorMode = CMODECMAP;
    }
    else
    {
	curColorMode = CMODERGB;
    }

#ifdef GRAPHICS
    /*Set shading model*/
    shademodel(shadeModels[curColorMode][curLightShading][curColorShading]);

    if ((curLightShading == DEPTHCUELIGHT) && hasDepthCue)
    {
	/*Set depth cueing*/
	depthcue(TRUE);
    }
    else if (overrideColor == false)
#endif
    {
	/*Set the default color for skeleton*/
	if (curSpace)
	{
	    ObjPtr panel, color, var;
	    real rgb[3];
    
	    panel = GetVar(curSpace, BACKPANEL);
	    if (panel)
	    {
		var = GetVar(panel, BACKGROUND);
		if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
		{
		    Array2CArray(rgb, var);
		}
		else if (var && IsInt(var))
		{
		    rgb[0] = uiColors[GetInt(var)][0] / 255.0;
		    rgb[1] = uiColors[GetInt(var)][1] / 255.0;
		    rgb[2] = uiColors[GetInt(var)][2] / 255.0;
		}
		else
		{
		    rgb[0] = rgb[1] = rgb[2] = 0.0;
		}
	    }
	    if (rgb[0] + rgb[1] + rgb[2] > 1.5)
	    {
		SetUIColor(UIBLACK);
	    }
	    else
	    {
		SetUIColor(UIWHITE);
	    }
	}
    }
}

void DoneDrawing()
/*Done drawing a picture*/
{
#ifdef GRAPHICS
    if (hasDepthCue)
    {
	depthcue(FALSE);
    }
#endif
}

void ZeroPictureTime()
/*Zeroes the picture time*/
{
    pictureTime = 0;
}

long GetPictureTime()
/*Gets the picture time*/
{
    return pictureTime;
}

#ifdef HAVE_PROTOTYPES
void DrawPicture(ObjPtr pic, Bool isTransparent, int lightShading, int colorShading, ObjPtr colors)
#else
void DrawPicture(pic, isTransparent, lightShading, colorShading, colors)
ObjPtr pic;
Bool isTransparent;
int lightShading;
int colorShading;
ObjPtr colors;
#endif
/*Draws picture pic.  isTransparent if transparent*/
{
#ifdef GRAPHICS
    PicItemPtr curItem;
    ObjPtr repObj;
    DrawFunc drawFunc;
    int qualIndex;
    long beginTime;
    struct tms buffer;

    beginTime = times(&buffer);

    /*Calculate the quality index*/
    qualIndex = drawingQuality;

    /*Make the picture deformed*/
    MakeVar(pic, PICDEFORMED);

    /*Prepare to draw*/
    PrepareToDraw(isTransparent, lightShading, colorShading, colors);

    switch (curColorMode)
    {
	case CMODECMAP:
		if (lightShading == DEPTHCUELIGHT)
		{
		    PrepareToDrawCDepthCue;
		}
		else
		{
		    PrepareToDrawC;
		}
		break;
	case CMODERGB:
		if (lightShading == DEPTHCUELIGHT)
		{
		    PrepareToDrawRGBDepthCue;
		}
		else
		{
		    PrepareToDrawRGB;
		}
		break;
    }
    if (qualIndex == DQ_DOTS)
    {
	switch (curColorMode)
	{
	    case CMODECMAP:
#define TRAVERSE TRAVERSEDOTS
#include "ScianStdCDraw.h"
#undef TRAVERSE
		break;
	    case CMODERGB:
#define TRAVERSE TRAVERSEDOTS
#include "ScianStdRGBDraw.h"
#undef TRAVERSE
		break;
	}
    }
    else
    {
	curItem = ((PicPtr) pic) -> items;
    
	switch (curColorMode)
	{
	    case CMODECMAP:
		while (curItem)
		{
		    int proximity;
		    int k;
		    if (curItem -> type < 0 || curItem -> type >= NPICITEMTYPES)
		    {
			char errStr[256];
			sprintf(errStr, "Item type %d not valid", curItem -> type);
			ReportError("DrawPicture", errStr);
			continue;
		    }
		    proximity = curItem -> proximity;
		    if (proximity > 5 || proximity < -5)
		    {
			ReportError("DrawPicture", "Bad proximity");
			continue;
		    }
		    if (proximity > 0)
		    {
			for (k = 0; k < proximity; ++k)
			{
			    NudgeCloser();
			}
		    }
		    else if (proximity < 0)
		    {
			for (k = 0; k > proximity; --k)
			{
			    NudgeFarther();
			}
		    }
		    drawFunc = drawFuncs[curItem -> type][qualIndex][0];
		    if (drawFunc)
		    {
			(*drawFunc)(curItem);
		    }
		    if (proximity > 0)
		    {
			for (k = 0; k < proximity; ++k)
			{
			    NudgeFarther();
			}
		    }
		    else if (proximity < 0)
		    {
			for (k = 0; k > proximity; --k)
			{
			    NudgeCloser();
			}
		    }
		    curItem = curItem -> next;
		}
		break;
	    case CMODERGB:
		if (overrideColor)
		{
		    lmcolor(LMC_NULL);
		}
		else if (lightShading == NOLIGHT)
		{
		    lmcolor(LMC_COLOR);
		}
		else
		{
		    lmcolor(LMC_DIFFUSE);
		}
    
		while (curItem)
		{
		    int proximity;
		    int k;
		    if (curItem -> type < 0 || curItem -> type >= NPICITEMTYPES)
		    {
			char errStr[256];
			sprintf(errStr, "Item type %d not valid", curItem -> type);
			ReportError("DrawPicture", errStr);
			continue;
		    }
		    proximity = curItem -> proximity;
		    if (proximity > 5 || proximity < -5)
		    {
			ReportError("DrawPicture", "Bad proximity");
			continue;
		    }
		    if (proximity > 0)
		    {
			for (k = 0; k < proximity; ++k)
			{
			    NudgeCloser();
			}
		    }
		    else if (proximity < 0)
		    {
			for (k = 0; k > proximity; --k)
			{
			    NudgeFarther();
			}
		    }
		    drawFunc = drawFuncs[curItem -> type][qualIndex][1];
		    (*drawFunc)(curItem);
		    if (proximity > 0)
		    {
			for (k = 0; k < proximity; ++k)
			{
			    NudgeFarther();
			}
		    }
		    else if (proximity < 0)
		    {
			for (k = 0; k > proximity; --k)
			{
			    NudgeCloser();
			}
		    }
		    curItem = curItem -> next;
		}
		break;
	}
    }

    DoneDrawing();

    pictureTime += (times(&buffer) - beginTime);
#endif
}

#ifdef HAVE_PROTOTYPES
void SavePictureGeometry(char *picName, ObjPtr pic, ObjPtr colors)
#else
void SavePictureGeometry(picName, pic, colors)
char *picName;
ObjPtr pic;
ObjPtr colors;
#endif
/*Saves the geometry of picture pic into a file derived from picName*/
{
    PicItemPtr curItem;
    ObjPtr repObj;
    FILE *outFile;
    char fileName[256];
    int curColorIndex;
    float curNormal[3];
    int k;

    printf("Saving picture for %s\n", picName);
    strcpy(fileName, picName);
    strcat(fileName, ".c");

    outFile = fopen(fileName, "w");
    if (!outFile)
    {
	perror(fileName);
	return;
    }

    /*Make the picture deformed*/
    MakeVar(pic, PICDEFORMED);

    /*Prepare to draw*/
    SetPalette(colors);

    curItem = ((PicPtr) pic) -> items;

    curColorIndex = -1;
    curNormal[0] = curNormal[1] = curNormal[2] = 0.0;

    fprintf(outFile, "    o = genobj();\n");
    fprintf(outFile, "    makeobj(o);\n");

    while (curItem)
    {
	switch(curItem -> type)
	{
	    case POLYGON:
		{
		    PolyPtr poly;
		    VertexPtr vertex;

		    poly = (PolyPtr) curItem;

		    fprintf(outFile, "    bgnpolygon();\n");
		    for (k = 0; k < poly -> nVertices; ++k)
		    {
			vertex = poly -> vertices[k];

			if (vertex -> colorIndex != curColorIndex)
			{
			    curColorIndex = vertex -> colorIndex;

			    fprintf(outFile, "    c[0] = %d;\n", curColors[curColorIndex][0]);
			    fprintf(outFile, "    c[1] = %d;\n", curColors[curColorIndex][1]);
			    fprintf(outFile, "    c[2] = %d;\n", curColors[curColorIndex][2]);
			    fprintf(outFile, "    c3s(c);\n");
			}

			if (vertex -> normal[0] != curNormal[0] ||
			    vertex -> normal[1] != curNormal[1] ||
			    vertex -> normal[2] != curNormal[2])
			{
			    curNormal[0] = vertex -> normal[0];
			    curNormal[1] = vertex -> normal[1];
			    curNormal[2] = vertex -> normal[2];

			    fprintf(outFile, "    n[0] = %#g;\n", curNormal[0]);
			    fprintf(outFile, "    n[1] = %#g;\n", curNormal[1]);
			    fprintf(outFile, "    n[2] = %#g;\n", curNormal[2]);
			    fprintf(outFile, "    n3f(n);\n");
			}

			fprintf(outFile, "    v[0] = %#g;\n", vertex -> position[0]);
			fprintf(outFile, "    v[1] = %#g;\n", vertex -> position[1]);
			fprintf(outFile, "    v[2] = %#g;\n", vertex -> position[2]);
			fprintf(outFile, "    v3f(v);\n");
		    }
		    fprintf(outFile, "    endpolygon();\n\n");
		}
		break;
	    case RECTMESH:
		{
		    register long i, j, offset, iDim, jDim, nX;
		    register VertexPtr *vertices;
		    register VertexPtr vertex;
		    float position[3];		/*Position of center vertex*/
		    int colorIndex;		/*Color within color table*/
		    RectMeshPtr rectMesh;

		    rectMesh = (RectMeshPtr) curItem;

		    iDim = rectMesh -> xDim;
		    jDim = rectMesh -> yDim;
		    vertices = rectMesh -> vertices;
		    nX = jDim * 2 - 1;
		
		    for (i = 0; i < iDim - 1; ++i)
		    {
			for (j = 0; j < jDim - 1; ++j)
			{
			    fprintf(outFile, "    bgnpolygon();\n");

			    /*Lower left corner*/
			    offset = RECTMESHVERTEX(rectMesh, i, j);
			    vertex = vertices[offset];
    
			    if (vertex -> colorIndex != curColorIndex)
			    {
				curColorIndex = vertex -> colorIndex;
    
				fprintf(outFile, "    c[0] = %d;\n", curColors[curColorIndex][0]);
				fprintf(outFile, "    c[1] = %d;\n", curColors[curColorIndex][1]);
				fprintf(outFile, "    c[2] = %d;\n", curColors[curColorIndex][2]);
				fprintf(outFile, "    c3s(c);\n");
			    }
    
			    if (vertex -> normal[0] != curNormal[0] ||
				vertex -> normal[1] != curNormal[1] ||
				vertex -> normal[2] != curNormal[2])
			    {
				curNormal[0] = vertex -> normal[0];
				curNormal[1] = vertex -> normal[1];
				curNormal[2] = vertex -> normal[2];
    
				fprintf(outFile, "    n[0] = %#g;\n", curNormal[0]);
				fprintf(outFile, "    n[1] = %#g;\n", curNormal[1]);
				fprintf(outFile, "    n[2] = %#g;\n", curNormal[2]);
				fprintf(outFile, "    n3f(n);\n");
			    }
    
			    fprintf(outFile, "    v[0] = %#g;\n", vertex -> position[0]);
			    fprintf(outFile, "    v[1] = %#g;\n", vertex -> position[1]);
			    fprintf(outFile, "    v[2] = %#g;\n", vertex -> position[2]);
			    fprintf(outFile, "    v3f(v);\n");

			    /*Lower right corner*/
			    offset = RECTMESHVERTEX(rectMesh, i + 1, j);
			    vertex = vertices[offset];
    
			    if (vertex -> colorIndex != curColorIndex)
			    {
				curColorIndex = vertex -> colorIndex;
    
				fprintf(outFile, "    c[0] = %d;\n", curColors[curColorIndex][0]);
				fprintf(outFile, "    c[1] = %d;\n", curColors[curColorIndex][1]);
				fprintf(outFile, "    c[2] = %d;\n", curColors[curColorIndex][2]);
				fprintf(outFile, "    c3s(c);\n");
			    }
    
			    if (vertex -> normal[0] != curNormal[0] ||
				vertex -> normal[1] != curNormal[1] ||
				vertex -> normal[2] != curNormal[2])
			    {
				curNormal[0] = vertex -> normal[0];
				curNormal[1] = vertex -> normal[1];
				curNormal[2] = vertex -> normal[2];
    
				fprintf(outFile, "    n[0] = %#g;\n", curNormal[0]);
				fprintf(outFile, "    n[1] = %#g;\n", curNormal[1]);
				fprintf(outFile, "    n[2] = %#g;\n", curNormal[2]);
				fprintf(outFile, "    n3f(n);\n");
			    }
    
			    fprintf(outFile, "    v[0] = %#g;\n", vertex -> position[0]);
			    fprintf(outFile, "    v[1] = %#g;\n", vertex -> position[1]);
			    fprintf(outFile, "    v[2] = %#g;\n", vertex -> position[2]);
			    fprintf(outFile, "    v3f(v);\n");
		
			    /*Upper right corner*/
			    offset = RECTMESHVERTEX(rectMesh, i + 1, j + 1);
			    vertex = vertices[offset];
    
			    if (vertex -> colorIndex != curColorIndex)
			    {
				curColorIndex = vertex -> colorIndex;
    
				fprintf(outFile, "    c[0] = %d;\n", curColors[curColorIndex][0]);
				fprintf(outFile, "    c[1] = %d;\n", curColors[curColorIndex][1]);
				fprintf(outFile, "    c[2] = %d;\n", curColors[curColorIndex][2]);
				fprintf(outFile, "    c3s(c);\n");
			    }
    
			    if (vertex -> normal[0] != curNormal[0] ||
				vertex -> normal[1] != curNormal[1] ||
				vertex -> normal[2] != curNormal[2])
			    {
				curNormal[0] = vertex -> normal[0];
				curNormal[1] = vertex -> normal[1];
				curNormal[2] = vertex -> normal[2];
    
				fprintf(outFile, "    n[0] = %#g;\n", curNormal[0]);
				fprintf(outFile, "    n[1] = %#g;\n", curNormal[1]);
				fprintf(outFile, "    n[2] = %#g;\n", curNormal[2]);
				fprintf(outFile, "    n3f(n);\n");
			    }
    
			    fprintf(outFile, "    v[0] = %#g;\n", vertex -> position[0]);
			    fprintf(outFile, "    v[1] = %#g;\n", vertex -> position[1]);
			    fprintf(outFile, "    v[2] = %#g;\n", vertex -> position[2]);
			    fprintf(outFile, "    v3f(v);\n");

			    /*Upper left corner*/
			    offset = RECTMESHVERTEX(rectMesh, i, j + 1);
			    vertex = vertices[offset];
    
			    if (vertex -> colorIndex != curColorIndex)
			    {
				curColorIndex = vertex -> colorIndex;
    
				fprintf(outFile, "    c[0] = %d;\n", curColors[curColorIndex][0]);
				fprintf(outFile, "    c[1] = %d;\n", curColors[curColorIndex][1]);
				fprintf(outFile, "    c[2] = %d;\n", curColors[curColorIndex][2]);
				fprintf(outFile, "    c3s(c);\n");
			    }
    
			    if (vertex -> normal[0] != curNormal[0] ||
				vertex -> normal[1] != curNormal[1] ||
				vertex -> normal[2] != curNormal[2])
			    {
				curNormal[0] = vertex -> normal[0];
				curNormal[1] = vertex -> normal[1];
				curNormal[2] = vertex -> normal[2];
    
				fprintf(outFile, "    n[0] = %#g;\n", curNormal[0]);
				fprintf(outFile, "    n[1] = %#g;\n", curNormal[1]);
				fprintf(outFile, "    n[2] = %#g;\n", curNormal[2]);
				fprintf(outFile, "    n3f(n);\n");
			    }
    
			    fprintf(outFile, "    v[0] = %#g;\n", vertex -> position[0]);
			    fprintf(outFile, "    v[1] = %#g;\n", vertex -> position[1]);
			    fprintf(outFile, "    v[2] = %#g;\n", vertex -> position[2]);
			    fprintf(outFile, "    v3f(v);\n");
		
			    fprintf(outFile, "    endpolygon();\n\n");
			}
		    }
		}
		break;
	    case POLYGONS:
		{
		    PolyPtr poly;
		    VertexPtr vertex;

		    for (poly = ((PolysPtr) curItem) -> polygons;
			 poly;
			 poly = (PolyPtr) (poly -> item . next))
		    {
			fprintf(outFile, "    bgnpolygon();\n");
			for (k = 0; k < poly -> nVertices; ++k)
			{
			    vertex = poly -> vertices[k];
    
			    if (vertex -> colorIndex != curColorIndex)
			    {
				curColorIndex = vertex -> colorIndex;
    
				fprintf(outFile, "    c[0] = %d;\n", curColors[curColorIndex][0]);
				fprintf(outFile, "    c[1] = %d;\n", curColors[curColorIndex][1]);
				fprintf(outFile, "    c[2] = %d;\n", curColors[curColorIndex][2]);
				fprintf(outFile, "    c3s(c);\n");
			    }
    
			    if (vertex -> normal[0] != curNormal[0] ||
				vertex -> normal[1] != curNormal[1] ||
				vertex -> normal[2] != curNormal[2])
			    {
				curNormal[0] = vertex -> normal[0];
				curNormal[1] = vertex -> normal[1];
				curNormal[2] = vertex -> normal[2];
    
				fprintf(outFile, "    n[0] = %#g;\n", curNormal[0]);
				fprintf(outFile, "    n[1] = %#g;\n", curNormal[1]);
				fprintf(outFile, "    n[2] = %#g;\n", curNormal[2]);
				fprintf(outFile, "    n3f(n);\n");
			    }
    
			    fprintf(outFile, "    v[0] = %#g;\n", vertex -> position[0]);
			    fprintf(outFile, "    v[1] = %#g;\n", vertex -> position[1]);
			    fprintf(outFile, "    v[2] = %#g;\n", vertex -> position[2]);
			    fprintf(outFile, "    v3f(v);\n");
			}
			fprintf(outFile, "    endpolygon();\n\n");

			if (poly == ((PolysPtr) curItem) -> lastItem)
			{
			    break;
			}
		    }
		}
	}
	curItem = curItem -> next;
    }
    fprintf(outFile, "    closeobj();\n");
    fclose(outFile);
}
static PolysPtr curPolys;
static ObjPtr curPicture;
static int curColorIndex;

void QuadrantPolys(r1, r2, r3, level)
float r1[3];
float r2[3];
float r3[3];
int level;
/*Emits a quadrant of sphere at center curCenter with radius curRadius given 
  radial normal vectors r1, r2, and r3, using current color table, at recursion 
  level level into curPolys*/
{
    if (level >= curSub)
    {
	/*Maximum number of subdivisions has been reached*/
	Vertex *v[3];
	v[0] = NewVertex(curPicture, VF_SAMEPLACE);
	v[1] = NewVertex(curPicture, VF_SAMEPLACE);
	v[2] = NewVertex(curPicture, VF_SAMEPLACE);
	v[0] -> position[0] = curCenter[0] + r1[0] * curRadius;
	v[0] -> position[1] = curCenter[1] + r1[1] * curRadius;
	v[0] -> position[2] = curCenter[2] + r1[2] * curRadius;
	v[1] -> position[0] = curCenter[0] + r2[0] * curRadius;
	v[1] -> position[1] = curCenter[1] + r2[1] * curRadius;
	v[1] -> position[2] = curCenter[2] + r2[2] * curRadius;
	v[2] -> position[0] = curCenter[0] + r3[0] * curRadius;
	v[2] -> position[1] = curCenter[1] + r3[1] * curRadius;
	v[2] -> position[2] = curCenter[2] + r3[2] * curRadius;

	v[0] -> normal[0] = r1[0];
	v[0] -> normal[1] = r1[1];
	v[0] -> normal[2] = r1[2];
	v[1] -> normal[0] = r2[0];
	v[1] -> normal[1] = r2[1];
	v[1] -> normal[2] = r2[2];
	v[2] -> normal[0] = r3[0];
	v[2] -> normal[1] = r3[1];
	v[2] -> normal[2] = r3[2];

	v[0] -> colorIndex = curColorIndex;
	v[1] -> colorIndex = curColorIndex;
	v[2] -> colorIndex = curColorIndex;

	AppendSPolyToPolys(curPolys, 3, v);
    }
    else
    {
	/*Do another subdivision*/
	float r12[3], r23[3], r31[3];	/*Inner triangle subdivisions*/
	r12[0] = (r1[0] + r2[0]);
	r12[1] = (r1[1] + r2[1]);
	r12[2] = (r1[2] + r2[2]);
	r23[0] = (r2[0] + r3[0]);
	r23[1] = (r2[1] + r3[1]);
	r23[2] = (r2[2] + r3[2]);
	r31[0] = (r3[0] + r1[0]);
	r31[1] = (r3[1] + r1[1]);
	r31[2] = (r3[2] + r1[2]);
	NORMALIZE(r12);
	NORMALIZE(r23);
	NORMALIZE(r31);

	/*Draw the subdivisions*/
	QuadrantPolys(r1, r12, r31, level + 1);
	QuadrantPolys(r12, r2, r23, level + 1);
	QuadrantPolys(r23, r3, r31, level + 1);
	QuadrantPolys(r12, r23, r31, level + 1);
    }
}

#ifdef HAVE_PROTOTYPES
PolysPtr ConvertSphereOntoPicture(ObjPtr picture, VertexPtr center, real radius, int flags, int nSubdivisions)
#else
PolysPtr ConvertSphereOntoPicture(picture, center, radius, flags, nSubdivisions)
ObjPtr picture;
VertexPtr center;
real radius;
int flags;
int nSubdivisions;
#endif
/*Returns a polys object of sphere*/
{
    float vx[3], vy[3], vz[3]; /*Vertices stuck out on those axes*/
    VertexPtr center2;

    curSub = nSubdivisions;	    
    curPolys = AppendPolysToPicture(picture);
    curPicture = picture;
    curPolys -> enclosed = true;

    /*Make the center point*/

    center2 = NewVertex(picture, flags);
    center2 -> colorIndex = center -> colorIndex;
    center2 -> position[0] = center -> position[0];
    center2 -> position[1] = center -> position[1];
    center2 -> position[2] = center -> position[2];


    /*Make the eight quadrants of the sphere*/
    /* +x +y +z */
    vx[0] = 1.0;
    vx[1] = 0.0;
    vx[2] = 0.0;
    vy[0] = 0.0;
    vy[1] = 1.0;
    vy[2] = 0.0;
    vz[0] = 0.0;
    vz[1] = 0.0;
    vz[2] = 1.0;
	
    curRadius = radius;
    curCenter[0] = center -> position[0];
    curCenter[1] = center -> position[1];
    curCenter[2] = center -> position[2];
    curColorIndex = center -> colorIndex;
    QuadrantPolys(vx, vy, vz, 0);
	
    /* +y -x +z*/
    vx[0] = -1.0;
    QuadrantPolys(vy, vx, vz, 0);
	
    /* -x -y -z*/
    vy[1] = -1.0;
    QuadrantPolys(vx, vy, vz, 0);
	
    /* -y +x +z*/
    vx[0] = 1.0;
    QuadrantPolys(vy, vx, vz, 0);
	
    /* +x -y -z*/
    vz[2] = -1.0;
    QuadrantPolys(vx, vy, vz, 0);
	
    /* +y +x -z*/
    vy[1] = 1.0;
    QuadrantPolys(vy, vx, vz, 0);
	
    /* -x +y -z*/
    vx[0] = -1.0;
    QuadrantPolys(vx, vy, vz, 0);
	
    /* -y, -x, -z*/
    vy[1] = -1.0;
    QuadrantPolys(vy, vx, vz, 0);

    return curPolys;
}

#ifdef HAVE_PROTOTYPES
void ConvertFrustumOntoPicture(ObjPtr picture, float end1[3], float rad1,
		float end2[3], float rad2, int nSubdivisions, Bool capEnds)
#else
void ConvertFrustumOntoPicture(picture, end1, rad1, end2, rad2, nSubdivisions, capEnds)
float end1[3], end2[3];
float rad1, rad2;
ObjPtr picture;
int nSubdivisions;
Bool capEnds;
#endif
/*Converts a frustum onto a picture*/
{
    float a[3];		/*Axial unit vector*/
    float r[MAXCYLSIDES][3];
    float s[3], n[3], f[3];
    Vertex v[MAXCYLSIDES + 1];
    double length;	/*Length of cylinder*/
    double nf;		/*Normalization factor*/
			/*Radial units around cylinder*/
    int end;		/*Counter for which end*/
    int b;		/*Counter around radius*/
    int d;		/*Delta for subdivision*/
    int k;		/*Random counter*/
    RectMeshPtr rectMesh;
    VertexPtr vertex;

    curNSides = 4;
    while (nSubdivisions--) curNSides *= 2;

    if (end2[0] == end1[0] &&
	end2[1] == end1[1] &&
	end2[2] == end1[2])
    {
	/*can't do it*/
	return;
    }

    /*First make an axial unit vector*/
    a[0] = end2[0] -
	   end1[0];
    a[1] = end2[1] -
	   end1[1];
    a[2] = end2[2] -
	   end1[2];
    length = sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
    nf = 1.0 / length;
    a[0] *= nf; a[1] *= nf; a[2] *= nf;

    /*See if it's nearly colinear with i*/
    if (ABS(a[0]) > 0.8)
    {
	/*It is, so cross by j to get first r*/
	r[0][0] = -a[2];
	r[0][1] = 0.0;
	r[0][2] = a[0];
    }
    else
    {
	/*It isn't, so cross by i to get first r*/
	r[0][0] = 0.0;
	r[0][1] = a[2];
	r[0][2] = -a[1];
    }
    
    /*Normalize r*/
    length = sqrt(r[0][0]*r[0][0] + r[0][1]*r[0][1] + r[0][2]*r[0][2]);
    nf = 1.0 / length;
    r[0][0] *= nf; r[0][1] *= nf, r[0][2] *= nf;

    /*Cross a with first radial unit to get orthogonal unit*/
    CROSS(a, r[0], r[curNSides / 4]);

    /*Fill out point radii 3 and 4 by reversing 1 and 2*/
    r[curNSides / 2][0] = -r[0][0];
    r[curNSides / 2][1] = -r[0][1];
    r[curNSides / 2][2] = -r[0][2];

    r[curNSides / 4 * 3][0] = -r[curNSides / 4][0];
    r[curNSides / 4 * 3][1] = -r[curNSides / 4][1];
    r[curNSides / 4 * 3][2] = -r[curNSides / 4][2];

    /*Subdivide the sides of the cylinder*/
    k = 0;
    for (d = curNSides / 4; d > 1; d /= 2)
    {
	register double cef_2;	/*Chord extension factor/2*/
	
	cef_2 = cefs_2[k];
	++k;
	
	for (b = 0; b < curNSides; b += d)
	{
	    register int m, e;	/*Midpoint and endpoint*/

	    /*Interpolate center of chord*/
	    e = (b + d) & (curNSides - 1);	/*Clip circular*/
	    m = b + d / 2;
	    r[m][0] = (r[b][0] + r[e][0]) * cef_2;
	    r[m][1] = (r[b][1] + r[e][1]) * cef_2;
	    r[m][2] = (r[b][2] + r[e][2]) * cef_2;
	}
    }

    /*Convert the cylinder*/
    rectMesh = AppendRectMeshToPicture(picture, 2, curNSides + 1, false);

    f[0] = rad2 * r[curNSides - 1][0] + end2[0] - 
	   (rad1 * r[curNSides - 1][0] + end1[0]);
    f[1] = rad2 * r[curNSides - 1][1] + end2[1] - 
	   (rad1 * r[curNSides - 1][1] + end1[1]);
    f[2] = rad2 * r[curNSides - 1][2] + end2[2] - 
	   (rad1 * r[curNSides - 1][2] + end1[2]);
    CROSS(f, r[curNSides - 1], s);
    CROSS(s, f, n);
    NORMALIZE(n);

    vertex = RectMeshVertex(rectMesh, 0, 0);
    vertex -> position[0] = rad2 * r[curNSides - 1][0] + end2[0];
    vertex -> position[1] = rad2 * r[curNSides - 1][1] + end2[1];
    vertex -> position[2] = rad2 * r[curNSides - 1][2] + end2[2];

#if 0
    vertex -> normal[0] = r[curNSides - 1][0];
    vertex -> normal[1] = r[curNSides - 1][1];
    vertex -> normal[2] = r[curNSides - 1][2];
#else
    vertex -> normal[0] = n[0];
    vertex -> normal[1] = n[1];
    vertex -> normal[2] = n[2];
#endif

    vertex = RectMeshVertex(rectMesh, 1, 0);
    vertex -> position[0] = rad1 * r[curNSides - 1][0] + end1[0];
    vertex -> position[1] = rad1 * r[curNSides - 1][1] + end1[1];
    vertex -> position[2] = rad1 * r[curNSides - 1][2] + end1[2];

#if 0
    vertex -> normal[0] = r[curNSides - 1][0];
    vertex -> normal[1] = r[curNSides - 1][1];
    vertex -> normal[2] = r[curNSides - 1][2];
#else
    vertex -> normal[0] = n[0];
    vertex -> normal[1] = n[1];
    vertex -> normal[2] = n[2];
#endif

    for (k = 0; k < curNSides; ++k)
    {
	f[0] = rad2 * r[k][0] + end2[0] - 
	       (rad1 * r[k][0] + end1[0]);
	f[1] = rad2 * r[k][1] + end2[1] - 
	       (rad1 * r[k][1] + end1[1]);
	f[2] = rad2 * r[k][2] + end2[2] - 
	       (rad1 * r[k][2] + end1[2]);
	CROSS(f, r[k], s);
	CROSS(s, f, n);
	NORMALIZE(n);

	vertex = RectMeshVertex(rectMesh, 0, k + 1);	
	vertex -> position[0] = rad2 * r[k][0] + end2[0];
	vertex -> position[1] = rad2 * r[k][1] + end2[1];
	vertex -> position[2] = rad2 * r[k][2] + end2[2];

#if 0
	vertex -> normal[0] = r[k][0];
	vertex -> normal[1] = r[k][1];
	vertex -> normal[2] = r[k][2];
#else
	vertex -> normal[0] = n[0];
	vertex -> normal[1] = n[1];
	vertex -> normal[2] = n[2];
#endif

	vertex = RectMeshVertex(rectMesh, 1, k + 1);	
	vertex -> position[0] = rad1 * r[k][0] + end1[0];
	vertex -> position[1] = rad1 * r[k][1] + end1[1];
	vertex -> position[2] = rad1 * r[k][2] + end1[2];

#if 0
	vertex -> normal[0] = r[k][0];
	vertex -> normal[1] = r[k][1];
	vertex -> normal[2] = r[k][2];
#else
	vertex -> normal[0] = n[0];
	vertex -> normal[1] = n[1];
	vertex -> normal[2] = n[2];
#endif
    }

    InterpRectCenters(rectMesh);

    if (capEnds)
    {
	/*Do top cap*/
	for (k = 0; k < curNSides; ++k)
	{
	    v[k] . position[0] = rad2 * r[k][0] + end2[0];
	    v[k] . position[1] = rad2 * r[k][1] + end2[1];
	    v[k] . position[2] = rad2 * r[k][2] + end2[2];
	    v[k] . normal[0] = a[0];
	    v[k] . normal[1] = a[1];
	    v[k] . normal[2] = a[2];
	}
	AppendPolyToPicture(picture, curNSides, v);
    
	/*Do bottom cap*/
	for (k = 0; k < curNSides; ++k)
	{
	    v[k] . position[0] = rad1 * r[k][0] + end1[0];
	    v[k] . position[1] = rad1 * r[k][1] + end1[1];
	    v[k] . position[2] = rad1 * r[k][2] + end1[2];
	    v[k] . normal[0] = -a[0];
	    v[k] . normal[1] = -a[1];
	    v[k] . normal[2] = -a[2];
	}
	AppendPolyToPicture(picture, curNSides, v);
    }
}

#ifdef HAVE_PROTOTYPES
PicItemPtr ColorItemsByItems(PicItemPtr destItem, ObjPtr owner, PicItemPtr curItem)
#else
PicItemPtr ColorItemsByItems(dest, owner, curItem)
ObjPtr dest, pic;
ObjPtr owner;
PicItemPtr curItem;
#endif
/*Colors a picture with another picture, which must be the source picture*/
{
    ObjPtr var;
    Bool capEnds;

    capEnds = GetPredicate(owner, CAPENDSP);

    while (curItem)
    {
	if (!destItem)
	{
	    ReportError("ColorItemsByItems", "Pictures don't match");
	    break;
	}
	switch (curItem -> type)
	{
	    case POLYGON:
	    case POLYLINE:
		ColorItemWithIndex(destItem, 
			((PolyPtr) curItem) -> vertices[0] -> colorIndex);
		break;
	    case FRUSTUM:

		ColorItemWithIndex(destItem,
			((FrustumPtr) curItem) -> colorIndex);
		if (capEnds)
		{
		    /*Cap the ends*/
		    destItem = destItem -> next;
		    if (!destItem) break;
		    ColorItemWithIndex(destItem,
			((FrustumPtr) curItem) -> colorIndex);
		    destItem = destItem -> next;
		    if (!destItem) break;
		    ColorItemWithIndex(destItem,
			((FrustumPtr) curItem) -> colorIndex);
		}
		break;
	    case SPHERE:

		ColorItemWithIndex(destItem,
			((SpherePtr) curItem) -> centerVertex -> colorIndex);
		break;
	    case POLYGONS:

		{
		    PolyPtr polygons;
		    polygons = ((PolysPtr) curItem) -> polygons;
		    if (polygons)
		    {
			ColorItemWithIndex((PicItemPtr) polygons,
				polygons -> vertices[0] -> colorIndex);
		    }
		}
		break;
	    case RECTMESH:

		ColorItemWithIndex(destItem,
			((RectMeshPtr) curItem) -> vertices[0] -> colorIndex);
		break;

	}
	curItem = curItem -> next;
	destItem = destItem -> next;
    }
    return destItem;
}

#ifdef HAVE_PROTOTYPES
void ColorPictureByPicture(ObjPtr dest, ObjPtr owner, ObjPtr pic)
#else
void ColorPictureByPicture(dest, owner, pic)
ObjPtr dest, pic;
ObjPtr owner;
#endif
/*Colors a picture with another picture, which must be the source picture*/
{
    PicItemPtr curItem, destItem;

    curItem = ((PicPtr) pic) -> items;
    destItem = ((PicPtr) dest) -> items;
    ColorItemsByItems(destItem, owner, curItem);
}

#ifdef HAVE_PROTOTYPES
ObjPtr ConvertOntoPicture(ObjPtr retVal, ObjPtr pic, ObjPtr owner)
#else
ObjPtr ConvertOntoPicture(retVal, pic, owner)
ObjPtr retVal;
ObjPtr pic;
ObjPtr owner;
#endif
/*Returns a copy of pic converted into internal form on the end of retVal.  
  Owner can give info on conversion*/
{
    PicItemPtr curItem;
    ObjPtr var;
    int sphereSub;
    int frustumSub;
    Bool capEnds;

    var = GetIntVar("ConvertOntoPicture", owner, SPHERESUBDIV);
    if (var)
    {
	sphereSub = GetInt(var);
    }
    else
    {
	sphereSub = 2;
    }

    var = GetIntVar("ConvertOntoPicture", owner, FRUSTUMSUBDIV);
    if (var)
    {
	frustumSub = GetInt(var);
    }
    else
    {
	frustumSub = 2;
    }

    capEnds = GetPredicate(owner, CAPENDSP);

    curItem = ((PicPtr) pic) -> items;
    while(curItem)
    {
	switch (curItem -> type)
	{
	    case POLYGON:
		ConvertPolyOntoPicture(retVal, (PolyPtr) curItem);
		break;
	    case POLYLINE:
		ConvertPolyOntoPicture(retVal, (PolyPtr) curItem);
		break;
	    case FRUSTUM:
		ConvertFrustumOntoPicture(retVal, 
			((FrustumPtr) curItem) -> end1,
			((FrustumPtr) curItem) -> rad1,
			((FrustumPtr) curItem) -> end2,
			((FrustumPtr) curItem) -> rad2,
			frustumSub, capEnds);
		break;
	    case SPHERE:
		ConvertSphereOntoPicture(retVal,
			((SpherePtr) curItem) -> centerVertex,
			((SpherePtr) curItem) -> radius,
			0,
			sphereSub);
		break;
	    case POLYGONS:
		{
		    PolyPtr polygons;
		    PolysPtr polySet;
		    polySet = AppendPolysToPicture(retVal);
		    polygons = ((PolysPtr) curItem) -> polygons;
		    while (polygons)
		    {
			ConvertPolyOntoPolys(polySet, polygons);
			polygons = (PolyPtr) (polygons -> item . next);
		    }
		}
		break;
	    case RECTMESH:
		{
		    RectMeshPtr rectMesh;
		    long k;
		    rectMesh = AppendRectMeshToPicture(retVal,
			((RectMeshPtr) curItem) -> xDim, 
			((RectMeshPtr) curItem) -> yDim,
			((RectMeshPtr) curItem) -> inCenter);
		    for (k = 0; k < (((RectMeshPtr) curItem) -> xDim * (2 * ((RectMeshPtr) curItem) -> yDim - 1)); ++k)
		    {
			((RectMeshPtr) rectMesh) -> vertices[k] -> normal[0] =
			((RectMeshPtr) curItem) -> vertices[k] -> normal[0];
			((RectMeshPtr) rectMesh) -> vertices[k] -> normal[1] =
			((RectMeshPtr) curItem) -> vertices[k] -> normal[1];
			((RectMeshPtr) rectMesh) -> vertices[k] -> normal[2] =
			((RectMeshPtr) curItem) -> vertices[k] -> normal[2];
			((RectMeshPtr) rectMesh) -> vertices[k] -> position[0] =
			((RectMeshPtr) curItem) -> vertices[k] -> position[0];
			((RectMeshPtr) rectMesh) -> vertices[k] -> position[1] =
			((RectMeshPtr) curItem) -> vertices[k] -> position[1];
			((RectMeshPtr) rectMesh) -> vertices[k] -> position[2] =
			((RectMeshPtr) curItem) -> vertices[k] -> position[2];
			((RectMeshPtr) rectMesh) -> vertices[k] -> colorIndex =
			((RectMeshPtr) curItem) -> vertices[k] -> colorIndex;
		    }
		}
		break;
	}
	curItem = curItem -> next;
    }
    return retVal;
}

#ifdef HAVE_PROTOTYPES
ObjPtr ConvertPicture(ObjPtr pic, ObjPtr owner)
#else
ObjPtr ConvertPicture(pic, owner)
ObjPtr pic;
ObjPtr owner;
#endif
/*Returns a copy of pic converted into internal form.  Owner can give info on conversion*/
{
    ObjPtr retVal;
    retVal = NewPicture();
    ConvertOntoPicture(retVal, pic, owner);
    return retVal;
}

void GetPictureBounds(pic, bounds)
ObjPtr pic;
real bounds[6];
/*Returns the bounds of pic in bounds*/
{
    PicItemPtr curItem;
    bounds[0] = bounds[2] = bounds[4] = PLUSINF;
    bounds[1] = bounds[3] = bounds[5] = MINUSINF;

    curItem = ((PicPtr) pic) -> items;

    while (curItem)
    {
	switch(curItem -> type)
	{
	    case SPHERE:
		if (((SpherePtr) curItem) -> centerVertex -> position[0] -
		    ((SpherePtr) curItem) -> radius < bounds[0])
		{
		    bounds[0] = ((SpherePtr) curItem) -> centerVertex -> position[0] -
				((SpherePtr) curItem) -> radius;
		}
		if (((SpherePtr) curItem) -> centerVertex -> position[0] +
		    ((SpherePtr) curItem) -> radius > bounds[1])
		{
		    bounds[1] = ((SpherePtr) curItem) -> centerVertex -> position[0] +
				((SpherePtr) curItem) -> radius;
		}
		if (((SpherePtr) curItem) -> centerVertex -> position[1] -
		    ((SpherePtr) curItem) -> radius < bounds[2])
		{
		    bounds[2] = ((SpherePtr) curItem) -> centerVertex -> position[1] -
				((SpherePtr) curItem) -> radius;
		}
		if (((SpherePtr) curItem) -> centerVertex -> position[1] +
		    ((SpherePtr) curItem) -> radius > bounds[3])
		{
		    bounds[3] = ((SpherePtr) curItem) -> centerVertex -> position[1] +
				((SpherePtr) curItem) -> radius;
		}
		if (((SpherePtr) curItem) -> centerVertex -> position[2] -
		    ((SpherePtr) curItem) -> radius < bounds[4])
		{
		    bounds[4] = ((SpherePtr) curItem) -> centerVertex -> position[2] -
				((SpherePtr) curItem) -> radius;
		}
		if (((SpherePtr) curItem) -> centerVertex -> position[2] +
		    ((SpherePtr) curItem) -> radius > bounds[5])
		{
		    bounds[5] = ((SpherePtr) curItem) -> centerVertex -> position[2] +
				((SpherePtr) curItem) -> radius;
		}
		break;
	    case FRUSTUM:
		if (((FrustumPtr) curItem) -> end1[0] - 
		    ((FrustumPtr) curItem) -> rad1 < bounds[0])
		{
		    bounds[0] = ((FrustumPtr) curItem) -> end1[0] - 
		    		((FrustumPtr) curItem) -> rad1;
		}
		if (((FrustumPtr) curItem) -> end1[0] + 
		    ((FrustumPtr) curItem) -> rad1 > bounds[1])
		{
		    bounds[1] = ((FrustumPtr) curItem) -> end1[0] + 
		    		((FrustumPtr) curItem) -> rad1;
		}
		if (((FrustumPtr) curItem) -> end1[1] - 
		    ((FrustumPtr) curItem) -> rad1 < bounds[2])
		{
		    bounds[2] = ((FrustumPtr) curItem) -> end1[1] - 
		    		((FrustumPtr) curItem) -> rad1;
		}
		if (((FrustumPtr) curItem) -> end1[1] + 
		    ((FrustumPtr) curItem) -> rad1 > bounds[3])
		{
		    bounds[3] = ((FrustumPtr) curItem) -> end1[1] + 
		    		((FrustumPtr) curItem) -> rad1;
		}
		if (((FrustumPtr) curItem) -> end1[2] - 
		    ((FrustumPtr) curItem) -> rad1 < bounds[4])
		{
		    bounds[4] = ((FrustumPtr) curItem) -> end1[2] - 
		    		((FrustumPtr) curItem) -> rad1;
		}
		if (((FrustumPtr) curItem) -> end1[2] + 
		    ((FrustumPtr) curItem) -> rad1 > bounds[5])
		{
		    bounds[5] = ((FrustumPtr) curItem) -> end1[2] + 
		    		((FrustumPtr) curItem) -> rad1;
		}

		if (((FrustumPtr) curItem) -> end2[0] - 
		    ((FrustumPtr) curItem) -> rad2 < bounds[0])
		{
		    bounds[0] = ((FrustumPtr) curItem) -> end2[0] - 
		    		((FrustumPtr) curItem) -> rad2;
		}
		if (((FrustumPtr) curItem) -> end2[0] + 
		    ((FrustumPtr) curItem) -> rad2 > bounds[1])
		{
		    bounds[1] = ((FrustumPtr) curItem) -> end2[0] + 
		    		((FrustumPtr) curItem) -> rad2;
		}
		if (((FrustumPtr) curItem) -> end2[1] - 
		    ((FrustumPtr) curItem) -> rad2 < bounds[2])
		{
		    bounds[2] = ((FrustumPtr) curItem) -> end2[1] - 
		    		((FrustumPtr) curItem) -> rad2;
		}
		if (((FrustumPtr) curItem) -> end2[1] + 
		    ((FrustumPtr) curItem) -> rad2 > bounds[3])
		{
		    bounds[3] = ((FrustumPtr) curItem) -> end2[1] + 
		    		((FrustumPtr) curItem) -> rad2;
		}
		if (((FrustumPtr) curItem) -> end2[2] - 
		    ((FrustumPtr) curItem) -> rad2 < bounds[4])
		{
		    bounds[4] = ((FrustumPtr) curItem) -> end2[2] - 
		    		((FrustumPtr) curItem) -> rad2;
		}
		if (((FrustumPtr) curItem) -> end2[2] + 
		    ((FrustumPtr) curItem) -> rad2 > bounds[5])
		{
		    bounds[5] = ((FrustumPtr) curItem) -> end2[2] + 
		    		((FrustumPtr) curItem) -> rad2;
		}
		break;
	    case POLYGON:
	    case POLYLINE:
		{
		    int k;
		    VertexPtr *curVertex;

		    curVertex = ((PolyPtr) curItem) -> vertices;
		    for (k = 0; k < ((PolyPtr) curItem) -> nVertices; ++k)
		    {
			if ((*curVertex) -> position[0] < bounds[0])
			{
			    bounds[0] = (*curVertex) -> position[0];
			}
			if ((*curVertex) -> position[0] > bounds[1])
			{
			    bounds[1] = (*curVertex) -> position[0];
			}
			if ((*curVertex) -> position[1] < bounds[2])
			{
			    bounds[2] = (*curVertex) -> position[1];
			}
			if ((*curVertex) -> position[1] > bounds[3])
			{
			    bounds[3] = (*curVertex) -> position[1];
			}
			if ((*curVertex) -> position[2] < bounds[4])
			{
			    bounds[4] = (*curVertex) -> position[2];
			}
			if ((*curVertex) -> position[2] > bounds[5])
			{
			    bounds[5] = (*curVertex) -> position[2];
			}
			++curVertex;
		    }
		}
		break;
	    case POLYGONS:
		{
		    PolyPtr polygons;
		    polygons = ((PolysPtr) curItem) -> polygons;
		    while (polygons)
		    {
			int k;
			VertexPtr *curVertex;

			curVertex = polygons -> vertices;
			for (k = 0; k < polygons -> nVertices; ++k)
			{
			    if ((*curVertex) -> position[0] < bounds[0])
			    {
				bounds[0] = (*curVertex) -> position[0];
			    }
			    if ((*curVertex) -> position[0] > bounds[1])
			    {
				bounds[1] = (*curVertex) -> position[0];
			    }
			    if ((*curVertex) -> position[1] < bounds[2])
			    {
				bounds[2] = (*curVertex) -> position[1];
			    }
			    if ((*curVertex) -> position[1] > bounds[3])
			    {
				bounds[3] = (*curVertex) -> position[1];
			    }
			    if ((*curVertex) -> position[2] < bounds[4])
			    {
				bounds[4] = (*curVertex) -> position[2];
			    }
			    if ((*curVertex) -> position[2] > bounds[5])
			    {
				bounds[5] = (*curVertex) -> position[2];
			    }
			    ++curVertex;
			}
			polygons = (PolyPtr) (polygons -> item . next);
		    }
		}
		break;
	    case RECTMESH:
		{
		    register long i, j, offset;
		    VertexPtr *vertices;

		    vertices = ((RectMeshPtr) curItem) -> vertices;

		    for (i = 0; i < ((RectMeshPtr) curItem) -> xDim; ++i)
		    {
			for (j = 0; j < ((RectMeshPtr) curItem) -> yDim; ++j)
			{
			    offset = RECTMESHVERTEX((RectMeshPtr) curItem, i, j);
			    if (vertices[offset] -> position[0] < bounds[0])
			    {
				bounds[0] = vertices[offset] -> position[0];
			    }
			    if (vertices[offset] -> position[0] > bounds[1])
			    {
				bounds[1] = vertices[offset] -> position[0];
			    }
			    if (vertices[offset] -> position[1] < bounds[2])
			    {
				bounds[2] = vertices[offset] -> position[1];
			    }
			    if (vertices[offset] -> position[1] > bounds[3])
			    {
				bounds[3] = vertices[offset] -> position[1];
			    }
			    if (vertices[offset] -> position[2] < bounds[4])
			    {
				bounds[4] = vertices[offset] -> position[2];
			    }
			    if (vertices[offset] -> position[2] > bounds[5])
			    {
				bounds[5] = vertices[offset] -> position[2];
			    }
			}
		    }
		}
		break;
	}
	curItem = curItem -> next;
    }
}

#ifdef HAVE_PROTOTYPES
void OpenGraphObj(ObjPtr object)
#else
ObjPtr OpenGraphObj(object)
ObjPtr object;
#endif
/*Opens an existing graphical object*/
{
#ifdef GRAPHICS
#ifdef GRAPHOBJ
    makeobj(GetInt(object));
#endif
#endif
}

#ifdef HAVE_PROTOTYPES
void CloseGraphObj(ObjPtr object)
#else
void CloseGraphObj(object)
ObjPtr object;
#endif
/*Closes a graphical object*/
{
#ifdef GRAPHICS
#ifdef GRAPHOBJ
    closeobj();
#endif
#endif
}

#ifdef HAVE_PROTOTYPES
void DrawGraphObj(ObjPtr object)
#else
void DrawGraphObj(object)
ObjPtr object;
#endif
/*Draw a graphical object*/
{
#ifdef GRAPHICS
#ifdef GRAPHOBJ
    callobj(GetInt(object));
#endif
#endif
}

#ifdef GRAPHICS
#ifdef GRAPHOBJ
static ObjPtr CleanupGraphObj(object)
ObjPtr object;
/*Cleans up a graphical object*/
{
    delobj(GetInt(object));
    return ObjTrue;
}
#endif
#endif

#ifdef HAVE_PROTOTYPES
ObjPtr NewOpenGraphObj(void)
#else
ObjPtr NewOpenGraphObj()
#endif
/*Returns a pointer to a new, opened graphical object.
*/
{
#ifdef GRAPHICS
#ifdef GRAPHOBJ
    Object obj;
    ObjPtr retVal;

    obj = genobj();

    retVal = NewInt(obj);
    SetMethod(retVal, CLEANUP, CleanupGraphObj);
    OpenGraphObj(retVal);
    return retVal;
#endif
#endif
}
Modified: Sun Nov 17 17:00:00 1996 GMT
Page accessed 2717 times since Sat Apr 17 21:55:11 1999 GMT