#include <stdio.h>
#include <math.h>
#include <strings.h>

#include "../h/cdefs.h"
#include "grf4.h"
#include "grf4Internal.h"
#include "grf4GenObject.h"

#define SMALL 1e-7

#if HLS
extern int grf4HiddenLineMode;
#include "../rci/link.h"
#endif

TRSF *grf4TransPtr = 0;
TRSF grf4IdentTrsf = { 1.0, 0.0, 0.0,
		       0.0, 1.0, 0.0,
		       0.0, 0.0, 1.0, 
		       0.0, 0.0, 0.0 };

int grf4PerspectiveMode = 0;
float grf4NearPlane;
float grf4FarPlane;

static TRSF Trans;

static float scrXm;
static float scrXb;
static float scrYm;
static float scrYb;

static int winw;
static int winh;
static int directionOfY;

/* Begining of stuff for generating PostScript files */

static float psXm;
static float psXb;
static float psYm;
static float psYb;
static FILE* psFp;
static char* psName;
static int psFlags;

static float psMaxX;
static float psMaxY;
static float psMinX;
static float psMinY;

typedef struct {
#define PS_MOVE		1
#define PS_DRAW		2
	int com;
	float x, y;
} PsCom;

#define PCB_SIZE	128

typedef struct psComBlk {
	struct psComBlk *next;
	PsCom buf[PCB_SIZE];
	int num;
} PsComBlk;

static PsComBlk *pcbList;
static PsComBlk *pcbTail;

/* end of stuff for generating PostScript files */

struct boundingBox {
	int xmin;
	int xmax;
	int ymin;
	int ymax;
  };

#define MAXINT	((unsigned)~0 >> 1)

static clearBoundingBox ();
static mergeBoundingBoxes ();
static updateBoundingBox ();
static float polygonDepth();
static void noopFxn();
static void* noopDefFxn();
static long capacityFxn();

static clearBoundingBox (br)
struct boundingBox *br;
{
	br->xmin = MAXINT;
	br->xmax = -1;
	br->ymin = MAXINT;
	br->ymax = -1;
}

static mergeBoundingBoxes (br, b1, b2)
struct boundingBox *br, *b1, *b2;
{
	br->xmin = MIN(b1->xmin, b2->xmin);
	br->ymin = MIN(b1->ymin, b2->ymin);
	br->xmax = MAX(b1->xmax, b2->xmax);
	br->ymax = MAX(b1->ymax, b2->ymax);
}

static updateBoundingBox (br, x, y)
register struct boundingBox *br;
register int x, y;
{
	if (x < br->xmin)
	 { br->xmin = x;
	 }
	if (x > br->xmax)
	 { br->xmax = x;
	 }
	if (y < br->ymin)
	 { br->ymin = y;
	 }
	if (y > br->ymax)
	 { br->ymax = y;
	 }
}

struct boundingBox drawArea;
struct boundingBox lastFlushArea;

int curScrX = 0;
int curScrY = 0;

void gdrLoadTrans(Tr)
TRSF *Tr;
{
	grf4TransPtr = &Trans;
	Trans = *Tr;
}

#if HLS

static VECTOR *vectorList;
static SCR_POLYGON *polygonList;
static VECTOR *vector0;
static SCR_POLYGON *polygon0;

static SCR_POLYGON *curPoly;
static PEDGE *vrtxTail;
static PEVENT *peventTail;

#define EPSILON (1e-6)

gdrBeginPolygon(obj)
void* obj;
{
	curPoly = newPolygon();
	curPoly->edges = 0;
	curPoly->n = 0;
	curPoly->visible = 0;
	curPoly->objCnt = grf4GetDrawObjCnt();
	vrtxTail = 0;
	peventTail = 0;
	return ((int)curPoly);
}

gdrVrtx(x,y,z,visible)
float x,y,z;
int visible;
{
	PEDGE* v;
	PEVENT* ev;

	v = newPedge(x,y,z);
	if (!curPoly->edges)
	 { curPoly->edges = v;
	 }
	LINK_T (vrtxTail, v);
	v->visible = visible;
#if 0
	if (!visible && (!peventTail || peventTail->type == EXPOSE_EVENT))
	 { ev = newPevent (HIDE_EVENT, 0.0);
	   if (!curPoly->nextEvents)
	    { curPoly->nextEvents = ev;
	    }
	   LINK_T (peventTail, ev);
	 }
	else if (visible && peventTail && peventTail->type == HIDE_EVENT)
	 { ev = newPevent (EXPOSE_EVENT, 0.0);
	   LINK_T (peventTail, ev);
	 }
#endif
	curPoly->n++;
}

gdrNrml(x,y,z)
float x,y,z;
{
	curPoly->nrml[0] = x;
	curPoly->nrml[1] = y;
	curPoly->nrml[2] = z;
}

gdrEndPolygon()
{
	register PEDGE *vtx;
	register PEVENT *ev;
	register SCR_POLYGON *p = curPoly;
	float near, perp;

	if (vrtxTail)
	 { /* turn edge list into circular linked list */
	   vrtxTail->next = p->edges;
	   p->edges->prev = vrtxTail;

	   /* verify circular linked list */
	    { PEDGE *vtx, *p1, *p2;
	      int cnt = 0;
	      vtx = p->edges;
	      do
	       { cnt++;
		 if (vtx->prev->next != vtx ||
		     vtx->next->prev != vtx)
		  { printf ("Error: botched edge list pointers\n");
		    exit(-1);
		  }
		 vtx = vtx->next;
	       }
	      while (vtx != p->edges);
	      if (cnt != p->n)
	       { printf ("Error: botched edge list, cnt = %d vs. %d\n",
			  cnt, p->n);
		 exit(-1);
	       }
	    }

	   /* Intersect the polygon with the near plane */

	   if (clipPolygon(p))
	    { curPoly = 0;
	      vrtxTail = 0;
	      printf ("clipping polygon\n");
	      return;	/* clip out entire polygon */
	    }

	   /* now build the hide/expose list, and transform x and y
	      into screen coordinates. Build the bounding box at this
	      time also. */

	   vtx = p->edges;
	   perp = (grf4PerspectiveMode ? grf4NearPlane/vtx->ez : 1.0);
	   p->xmax = p->xmin = vtx->x*perp;
	   p->ymax = p->ymin = vtx->y*perp;
	   p->ezmax = p->ezmin = vtx->ez;
	   do
	    { if (!vtx->visible && vtx->prev->visible)
	       { ev = newPevent (HIDE_EVENT, 1.0);
		 vtx->prev->nextEvents = ev;
		 vtx->prevEvents = ev;
	       }
	      if (vtx->visible && !vtx->prev->visible)
	       { ev = newPevent (EXPOSE_EVENT, 1.0);
		 vtx->prev->nextEvents = ev;
		 vtx->prevEvents = ev;
	       }
	      if (grf4PerspectiveMode)
	       { vtx->x *= grf4NearPlane/vtx->ez;
		 vtx->y *= grf4NearPlane/vtx->ez;
	       }
	      if (vtx->visible)
	       { p->visible++;
	       }
	      if (vtx->x > p->xmax)
	       { p->xmax = vtx->x;
	       }
	      else if (vtx->x < p->xmin)
	       { p->xmin = vtx->x;
	       }
	      if (vtx->y > p->ymax)
	       { p->ymax = vtx->y;
	       }
	      else if (vtx->y < p->ymin)
	       { p->ymin = vtx->y;
	       }
	      if (vtx->ez > p->ezmax)
	       { p->ezmax = vtx->ez;
	       }
	      else if (vtx->ez < p->ezmin)
	       { p->ezmin = vtx->ez;
	       }
	      vtx = vtx->next;
	    }
	   while (vtx != p->edges);

	   if (!polygon0)
	    { polygon0 = p;
	    }
	   LINK_T (polygonList, p);
	   curPoly = 0;
	   vrtxTail = 0;
	 }
}

gdrVector(x1,y1,z1, x2,y2,z2)
float x1,y1,z1, x2,y2,z2;
{
	VECTOR* v;
	float epsilon;
	float near = grf4NearPlane;
	float r, p;

	epsilon = EPSILON*(grf4NearPlane-grf4FarPlane);
	v = newVector();

	/* remember that +z comes out towards the viewer, so that
	   z2 > z1 implies that z2 is in front of z1. */

	/* Make sure either z1 and/or z2 is at least epsilon behind
	   the clipping plane; otherwise, clip the whole vector. */

	if (near-z2 < epsilon && near-z1 < epsilon)
	 { return;
	 }

	/* Now, if z1 or z2 is in front of the clipping plane, clip 
	   the vector. */

	if (near-z2 < 0)
	 { r = (near-z1)/(z2-z1);
	   z2 = near;
	   x2 = x1+r*(x2-x1);
	   y2 = y1+r*(y2-y1);
	 }
	else if (near-z1 < 0)
	 { r = (near-z2)/(z1-z2);
	   z1 = near;
	   x1 = x2+r*(x1-x2);
	   y1 = y2+r*(y1-y2);
	 }

	/* scale the final coordinates of the vector and assign them to
	   the data structure. */

	p = (grf4PerspectiveMode ? near/z1 : 1.0);
	v->x1 = x1*p;
	v->y1 = y1*p;
	v->ez1 = z1;
	p = (grf4PerspectiveMode ? near/z2 : 1.0);
	v->x2 = x2*p;
	v->y2 = y2*p;
	v->ez2 = z2;

	if (!vector0)
	 { vector0 = v;
	 }
	LINK_T (vectorList, v);
}

#endif

static struct grf4fxnTable *fxntab;

static float curX;
static float curY;
static float curZ;

void gdrMove (x,y,z)
float x,y,z;
{
	TRSF *T;
	float a;

	if (T = grf4TransPtr)
	 { curX = T->n.x*x + T->o.x*y + T->a.x*z + T->p.x;
	   curY = T->n.y*x + T->o.y*y + T->a.y*z + T->p.y;
	   curZ = T->n.z*x + T->o.z*y + T->a.z*z + T->p.z;
	 }
	else
	 { curX = x;
	   curY = y;
	   curZ = z;
	 }
}

void gdrDraw (x,y,z)
float x,y,z;
{
	TRSF *T;
	float ex, ey, ez;
	float a;
	int scrX, scrY;

	if (T = grf4TransPtr)
	 { ex = T->n.x*x + T->o.x*y + T->a.x*z + T->p.x;
	   ey = T->n.y*x + T->o.y*y + T->a.y*z + T->p.y;
	   ez = T->n.z*x + T->o.z*y + T->a.z*z + T->p.z;
	 }
	else
	 { ex = x;
	   ey = y;
	   ez = z;
	 }
#if HLS
	if (grf4HiddenLineMode)
	 { gdrVector (curX, curY, curZ, ex, ey, ez);
	 }
	else
#endif
	 { 
	   grf4DrawVector (curX, curY, curZ, ex, ey, ez);
	 }

	curX = ex;
	curY = ey;
	curZ = ez;
}

void gdrMove2 (x,y)
float x,y;
{
	TRSF *T;
	float ex, ey, ez;
	float a;

	if (T = grf4TransPtr)
	 { curX = T->n.x*x + T->o.x*y + T->p.x;
	   curY = T->n.y*x + T->o.y*y + T->p.y;
	   curZ = T->n.z*x + T->o.z*y + T->p.z;
	 }
	else
	 { curX = x;
	   curY = y;
	   curZ = 0.0;
	 }
}

void gdrDraw2 (x,y)
float x,y;
{
	TRSF *T;
	float ex, ey, ez;
	float a;
	int scrX, scrY;

	if (T = grf4TransPtr)
	 { ex = T->n.x*x + T->o.x*y + T->p.x;
	   ey = T->n.y*x + T->o.y*y + T->p.y;
	   ez = T->n.z*x + T->o.z*y + T->p.z;
	 }
	else
	 { ex = x;
	   ey = y;
	   ez = 0.0;
	 }
#if HLS
	if (grf4HiddenLineMode)
	 { gdrVector (curX, curY, curZ, ex, ey, ez);
	 }
	else
#endif
	 { 
	   grf4DrawVector (curX, curY, curZ, ex, ey, ez);
	 }
	curX = ex;
	curY = ey;
	curZ = ez;
}

grf4DrawVector (x1, y1, z1, x2, y2, z2)
float x1, y1;
float x2, y2;
register float z1, z2;
{
	register float p1, p2;
	register float near;

	near = grf4NearPlane;
	if (z1 <= near && z2 <= near)
	 { p1 = (grf4PerspectiveMode ? near/z1 : 1.0);
	   p2 = (grf4PerspectiveMode ? near/z2 : 1.0);
	   grf4ScrMove (x1*p1, y1*p1);
	   grf4ScrDraw (x2*p2, y2*p2);
	 }
	else if (z1 <= near && z2 > near)
	 { p1 = (grf4PerspectiveMode ? near/z1 : 1.0);
	   p2 = (near-z2)/(z1-z2);
	   grf4ScrMove (x1*p1, y1*p1);
	   grf4ScrDraw (x2+(x1-x2)*p2, y2+(y1-y2)*p2);
	 }
	else if (z1 > near && z2 <= near)
	 { p1 = (near-z1)/(z2-z1);
	   p2 = (grf4PerspectiveMode ? near/z2 : 1.0);
	   grf4ScrMove (x1+(x2-x1)*p1, y1+(y2-y1)*p1);
	   grf4ScrDraw (x2*p2, y2*p2);
	 }
}

grf4ScrMove (ex, ey)
float ex, ey;
{
	curScrX = scrXm*ex + scrXb;
	curScrY = scrYm*ey + scrYb;
	
	updateBoundingBox (&drawArea, curScrX, curScrY);
}

grf4ScrDraw (ex, ey)
float ex, ey;
{
	register struct minmax *mp;
	int scrX, scrY;

	scrX = scrXm*ex + scrXb;
	scrY = scrYm*ey + scrYb;
	(*fxntab->pxlDraw)(curScrX, curScrY, scrX, scrY);
	curScrX = scrX;
	curScrY = scrY;

	updateBoundingBox (&drawArea, scrX, scrY);
}

void gdrClear()
{
	register struct boundingBox *da = &drawArea;
	int x1,x2,y1,y2;

#if HLS
	if (grf4HiddenLineMode)
	 { freePolygonMem();
	   vectorList = 0;
	   polygonList = 0;
	   vector0 = 0;
	   polygon0 = 0;
	 }
#endif
	 { 
	   if (da->xmax != -1)
	    { x1 = MAX(da->xmin,0);
	      y1 = MAX(da->ymin,0);
	      x2 = MIN(da->xmax,winw-1);
	      y2 = MIN(da->ymax,winh-1);
	      if (x2-x1+1 > 0 && y2-y1+1 > 0)
	       { (*fxntab->pxlClear)(x1, y1, x2-x1+1, y2-y1+1);
	       }
	    }
	   clearBoundingBox (da);
	 }
}

#if HLS
static float polygonDepth(poly,x,y)
SCR_POLYGON *poly;
float x, y;
{
	float ez;
	if (!computeDepth (&ez, poly->nrml, poly->edges->x, poly->edges->y,
			   poly->edges->ez, x, y))
	 { printf ("polygonDepth: can't do depth computation\n");
	   exit (-1);
	 }
	return (ez);
}

#endif

extern SCR_POLYGON *link1Face;
extern SCR_POLYGON *spherePoly;
extern SCR_POLYGON *conePoly;
extern SCR_POLYGON *discPoly;

void gdrFlush()
{
	struct boundingBox area; 
	int x1,x2,y1,y2;

#if HLS
	if (grf4HiddenLineMode)
	 { 
	   VECTOR *v;
	   SCR_POLYGON *p1, *p1_next;
	   float x, y;

	   vectorList = vector0;	/* reverse list order */
	   polygonList = polygon0;	/* reverse list order */
	   
	   /* intersect each vector with each polygon, and draw the result */

	   setScrEpsilon(ABS(winw/scrXm),ABS(winh/scrYm));

	   for (v=vectorList; v; v=v->next)
	    { for (p1=polygonList; p1; p1=p1->next)
	       { if (vectorIntersect(p1,v,&x,&y))
		  { float r;
		    r = (v->rInto+v->rOut)/2;
		    if (polygonDepth (p1,x,y) > (v->ez1 + r*(v->ez2-v->ez1)))
		     { vectorClip(v);
		     }
		  }
	       }
	      vectorDraw (v);
	    }

	   /* intersect each polygon with each other polygon, 
	      and draw the result */
	   for (p1=polygonList; p1; p1=p1_next)
	    { p1_next = p1->next;
	      checkRemainingPolys (p1, p1_next);
	    }
	   for (p1=polygonList; p1; p1=p1->next)
	    { drawPolygon(p1);
	    }
	   if (psFp)
	    { for (v=vectorList; v; v=v->next)
	       { PostScriptVector (v);
	       }
	      for (p1=polygonList; p1; p1=p1->next)
	       { PostScriptPolygon (p1);
	       }
	      PostScriptClose ();
	    }
	 }
#endif
	 { mergeBoundingBoxes (&area, &drawArea, &lastFlushArea);

	   if (area.xmax != -1)
	    { x1 = MAX(area.xmin,0);
	      y1 = MAX(area.ymin,0);
	      x2 = MIN(area.xmax,winw-1);
	      y2 = MIN(area.ymax,winh-1);
	      if (x2-x1+1 > 0 && y2-y1+1 > 0)
	       { (*fxntab->pxlFlush)(x1, y1, x2-x1+1, y2-y1+1);
	       }
	    }
	   lastFlushArea = drawArea;
	 }
}

#if HLS

checkRemainingPolys (p1, prest)
SCR_POLYGON *p1, *prest;
{
	SCR_POLYGON *p2, *p2_next;
	SCR_POLYGON *p1a, *p2a;

	for (p2=prest; p2; p2=p2_next)
	 { p2_next = p2->next;
	   checkPolygonPair (p1, &p1a, p2, &p2a);
	   if (p2a)
	    { /* second polygon was split */
	      LINK_BEFORE_H (polygonList, p2, p2a);
	    }
	   if (p1a)
	    { /* first polygon was split */
	      LINK_BEFORE_H (polygonList, p1, p1a);
	      checkRemainingPolys (p1a, p2_next);
	    }
	 }
}

checkPolygonPair (p1, p1a, p2, p2a)
SCR_POLYGON *p1, *p2;
SCR_POLYGON **p1a, **p2a;
{
	INTER_POLYGON isect;
	float x, y;
	int status;

	if (!p1->objCnt || !p2->objCnt || p1->objCnt != p2->objCnt)
	 { 
	   status = intersectPolygons(p1,p2,0,0,0,&x,&y,&isect);
	   switch (status)
	    { case P_DONT_INTERSECT:
	       { break;
	       }
	      case P_INTERSECT:
	       { PEDGE *ee;
		
		 if (isect.n%2)
		  { printf ("checkPolygonPair(): odd no. intersections\n");
		    exit (-1);
		  }

		 if (polygonDepth(p1,x,y) < polygonDepth(p2,x,y))
		  { obscurePolygon(p1,&isect);
		  }
		 else
		  { obscurePolygon(p2,&isect);
		  }
		 break;
	       }
	      case P_POLY1_INSIDE:
	       { 
		 if (polygonDepth(p1,x,y) < polygonDepth(p2,x,y))
		  { p1->visible = 0;
		  }
		 break;
	       }
	      case P_POLY2_INSIDE:
	       { 
		 if (polygonDepth(p2,x,y) < polygonDepth(p1,x,y))
		  { p2->visible = 0;
		  }
		 break;
	       }
	    }
	 }
	*p1a = NULL;
	*p2a = NULL;
}

#endif

static float virtL;
static float virtR;
static float virtB;
static float virtT;

void gdrWindow(l, r, b, t, n, f)
float l, r, b, t, n, f;
{
	virtL = l;
	virtR = r;
	virtB = b;
	virtT = t;

	scrXm = (winw-1)/(r-l);
	scrXb = 0.5-l*scrXm;

	if (directionOfY == -1)
	 { scrYm = (winh-1)/(b-t);
	   scrYb = 0.5-t*scrYm;
	 }
	else
	 { scrYm = (winh-1)/(t-b);
	   scrYb = 0.5-b*scrYm;
	 }
	grf4PerspectiveMode = 1;
	grf4NearPlane = -n;
	grf4FarPlane = -f;
}

void gdrOrtho(l, r, b, t, n, f)
float l, r, b, t, n, f;
{
	virtL = l;
	virtR = r;
	virtB = b;
	virtT = t;

	scrXm = (winw-1)/(r-l);
	scrXb = 0.5-l*scrXm;
	
	if (directionOfY == -1)
	 { scrYm = (winh-1)/(b-t);
	   scrYb = 0.5-t*scrYm;
	 }
	else
	 { scrYm = (winh-1)/(t-b);
	   scrYb = 0.5-b*scrYm;
	 }

	grf4PerspectiveMode = 0;
	grf4NearPlane = -n;
}

gdrInit(w, h, d)
int w, h, d;
{
	winw = w;
	winh = h;
	directionOfY = d;

	clearBoundingBox (&lastFlushArea);
	clearBoundingBox (&drawArea);
}

gdrResize (w, h)
int w, h;
{
	winw = w;
	winh = h;

	clearBoundingBox (&lastFlushArea);
	clearBoundingBox (&drawArea);
}

static void noopFxn()
{
}

static void* noopDefFxn()
{
	return ((void*)1);
}


static long capacityFxn()
{
	return(0);
}

gdrSetupFxns (ftab)
struct grf4fxnTable *ftab;
{
	ftab->capacity = capacityFxn;
	ftab->ortho = gdrOrtho;
	ftab->window = gdrWindow;
	ftab->loadTrans = gdrLoadTrans;

	ftab->clear = gdrClear;
	ftab->flush = gdrFlush;
	ftab->move = gdrMove;
	ftab->move2 = gdrMove2;
	ftab->draw = gdrDraw;
	ftab->draw2 = gdrDraw2;
	ftab->drawPrism = gdrDrawPrism;
	ftab->drawSphere = gdrDrawSphere;
	ftab->drawPolygon = gdrDrawPolygon;
	ftab->drawLineSet = gdrDrawLineSet;

	ftab->genDisc = gdrGenDisc;
	ftab->genCone = gdrGenCone;
	ftab->genPrism = gdrGenPrism;
	ftab->genSphere = gdrGenSphere;
	ftab->genPolygon = gdrGenPolygon;
	ftab->genRegPolygon = gdrGenRegPolygon;
	ftab->genLineSet = gdrGenLineSet;
	ftab->setDepth = gdrSetDepth;

	ftab->overPrism = gdrOverPrism;
	ftab->overSphere = gdrOverSphere;

	/* lighting and material functions are not implemented */

	ftab->defLight = noopDefFxn;
	ftab->defMaterial = noopDefFxn;
	ftab->defMaterialD = noopDefFxn;
	ftab->setLight = noopFxn;
	ftab->setMaterial = noopFxn;
	ftab->resetLights = noopFxn;

	fxntab = ftab;
}

#if HLS

clipPolygon(poly)
SCR_POLYGON *poly;
{
	float epsilon;
	PEDGE *start = 0;
	PEDGE *vn=0;
	PEDGE *vp=0;
	PEDGE *new;
	PEDGE *p1;
	float near;
	float r;

	epsilon = EPSILON*(grf4NearPlane-grf4FarPlane);
	near = grf4NearPlane;

	/* first, see if we can find a point that is definitely
	   behind the clipping plane. If we can't, then we clip the whole
	   polygon. */ 

	p1 = poly->edges;
	do
	 { if (p1->ez - near < epsilon)
	    { start = p1;
	      break;
	    }
	   p1 = p1->next;
	 }
	while (p1 != poly->edges);

	if (!start)
	 { return(1);
	 }

	/* Now proceed in each direction to find the crossings,
	   if any, with the near plane. */

	for (vn=start->next; vn->ez-near <= 0; vn=vn->next)
	 { if (vn == start)
	    { return (0);	/* polygon is totally visible. */
	    }
	 }
	for (vp=start->prev; vp->ez-near <= 0; vp=vp->prev)
	   ;
	p1 = vn->prev;
	if (ABS(vn->ez - p1->ez) > EPSILON)
	 { r = (near - p1->ez)/(vn->ez - p1->ez);
	   r = MIN(1.0,MAX(r,0.0));
	   if (r > EPSILON)
	    { new = newPedge(p1->x+r*(vn->x-p1->x), p1->y+r*(vn->y-p1->y), 
						    p1->ez+r*(vn->ez-p1->ez));
	      p1->next = new;
	      new->prev = p1;
	      new->visible = 0;
	      vn = new;
	    }
	 }
	else
	 { vn = p1;
	 }
	p1 = vp->next;
	if (ABS(vp->ez - p1->ez) > EPSILON)
	 { r = (near - p1->ez)/(vp->ez - p1->ez);
	   r = MIN(1.0,MAX(r,0.0));
	   if (r > EPSILON)
	    { new = newPedge(p1->x+r*(vp->x-p1->x), p1->y+r*(vp->y-p1->y), 
						    p1->ez+r*(vp->ez-p1->ez));
	      p1->prev = new;
	      new->next = p1;
	      new->visible = vp->visible;
	      vp = new;
	    }
	 }
	else
	 { vp = p1;
	 }
	vp->prev = vn;
	vn->next = vp;
	poly->edges = vn;
	return (0);
}

#if 0
obscurePolygon(poly)
SCR_POLYGON *poly;
{
	/* Here is the basic paradigm for adding a hidding region delimited 
	   by the interval [h,e]:

	   1. If the region at h is exposed, then add HIDE event at h
	   2. If the region at e is exposed, then add EXPOSE event at e
	   3. Delete all other events in the interval [h,e].

	   h corresponds to the value of rInto, and e corresponds to the value
	   of rOut. 
	 */

	PEDGE *ph, *pe, *phStart = 0;
	PEVENT *evFwd, *evBak;
	PEVENT *hide = 0;
	PEVENT *expose;
	float h, e;

	if (!poly->visible)
	 { return;
	 }

	/* make a first pass through to find the first hide event */

	for (ph=poly->edges; !(ph->intersect & INTO); ph=ph->next)
	   ;
	phStart = ph;
	do
	 { 
	   /* If the region at h is exposed, add the hide event */

	   h = ph->rInto;
	   evBak = 0;
	   for (evFwd=ph->nextEvents; evFwd && evFwd->r<(h-EPSILON); 
				      evFwd=evFwd->next)
	    { evBak = evFwd;
	    }
	   if ((!evBak && ph->visible) ||
	       (evBak && evBak->type == EXPOSE_EVENT))
	    { hide = newPevent(HIDE_EVENT,h);
	      if (evBak)
	       { evBak->next = hide;
	       }
	      else
	       { ph->nextEvents = hide;
	       }
	    }
	   else
	    { hide = evBak;
	    }
	   ph->intersect &= ~INTO;

	   /* find the edge with the corresponding OUT event */

	   pe = ph;
	   if (!(pe->intersect & OUT))
	    { pe = pe->next;
	      if (pe->intersect & INTO)
	       { printf ("obscure: (1) bad intersect=%d\n", pe->intersect);
		 exit (-1);
	       }
	      pe->visible = 0;
	      evFwd = pe->nextEvents;
	      while (!(pe->intersect & OUT))
	       { pe->nextEvents = 0;
		 pe = pe->next;
		 if (pe->intersect & INTO)
		  { printf ("obscure: (2) bad intersect=%d\n", pe->intersect);
		    exit (-1);
		  }
		 pe->prevEvents = 0;
		 pe->visible = 0;
		 evFwd = pe->nextEvents;
	       }
	    }

	   /* If the region at e is exposed, add the expose event */

	   e = pe->rOut;
	   for ( ; evFwd && evFwd->r<(e+EPSILON); evFwd=evFwd->next)
	      ;
	   if ((!evFwd && pe->next->visible) || 
	       (evFwd && evFwd->type == HIDE_EVENT))
	    { expose = newPevent(EXPOSE_EVENT,e);
	      expose->next = evFwd;
	      if (evFwd == NULL)
	       { pe->next->prevEvents = expose;
	       }
	    }
	   else
	    { expose = evFwd;
	    }
	   pe->intersect &= ~OUT;

	   if (ph == pe)
	    { if (hide)
	       { hide->next = expose;
	       }
	      else
	       { ph->nextEvents = expose;
	       }
	      if (expose == NULL)
	       { ph->next->prevEvents = hide;
	       }
	    }
	   else
	    { if (hide)
	       { hide->next = 0;
	       }
	      else
	       { ph->nextEvents = 0;
	       }
	      ph->next->prevEvents = hide;
	      pe->nextEvents = expose;
	      if (expose == NULL)
	       { pe->next->prevEvents = 0;
	       }
	    }

	   if (pe->intersect)
	    { printf ("obscure: (3) bad intersect=%d\n", pe->intersect);
	      exit (-1);
	    }

	   for (ph=pe->next; !(ph->intersect & INTO); ph=ph->next)
	    { if (ph == phStart)
	       { break;
	       }
	    }
	 }
	while (ph != phStart);

	/* Finally, go through the polygon and clean it up:
	   Delete any events that are very close together, and
	   count the total number of visible edges. */

	poly->visible = 0;
	ph = poly->edges;
	do
	 { if (ph->visible || ph->nextEvents)
	    { poly->visible++;
	    }
	   ph = ph->next;
	 }
	while (ph != poly->edges);

	/* sanity checks: */

	 { int visible;
	   float lastr;

	   ph = poly->edges;
	   visible = ph->visible;
	   do
	    { if (ph->intersect)
	       { printf ("obscurePolygon: edge not cleared, = %d\n",
			  ph->intersect);
		 exit (-1);
	       }
	      lastr = -1.0;
	      evBak = 0;
	      for (evFwd=ph->nextEvents; evFwd; evFwd=evFwd->next)
	       { if (lastr != -1.0 && lastr > evFwd->r)
		  { printf ("obscurePolygon: r out of order\n");
		    exit (-1);
		  }
		 if (evFwd->r < 0.0 || evFwd->r > 1.0)
		  { printf ("obscurePolygon: bad r value\n");
		    exit (-1);
		  }
		 if (evFwd->type == HIDE_EVENT)
		  { if (!visible)
		     { printf ("obscurePolygon: bad hide event\n");
		       exit (-1);
		     }
		    visible = 0;
		  }
		 else
		  { if (visible)
		     { printf ("obscurePolygon: bad expose event\n");
		       exit (-1);
		     }
		    visible = 1;
		  }
		 evBak = evFwd;
	       }	
	      if (evBak != ph->next->prevEvents)
	       { printf ("obscurePolygon: prevEvent != last event\n");
		 exit (-1);
	       }
	      if (visible != ph->next->visible)
	       { printf ("obscurePolygon: next vtx visible out of sync\n");
		 exit (-1);
	       }
	      ph = ph->next;
	    }
	   while (ph != poly->edges);
	}

}
#else
obscurePolygon(poly, intr)
SCR_POLYGON *poly;
INTER_POLYGON *intr;
{
	/* Here is the basic paradigm for adding a hidding region delimited 
	   by the interval [h,e]:

	   1. If the region at h is exposed, then add HIDE event at h
	   2. If the region at e is exposed, then add EXPOSE event at e
	   3. Delete all other events in the interval [h,e].

	   h corresponds to the value of rInto, and e corresponds to the value
	   of rOut. 
	 */

	PEDGE *ph, *pe, *ee;
	PEVENT *evFwd, *evBak;
	PEVENT *hide = 0;
	PEVENT *expose = 0;
	float h, e;

	IEDGE *ie;
	IEDGE *ieStart;

	if (!poly->visible)
	 { return;
	 }

	/* find the first hide point */

	ie = intr->edges;
	if (ie->poly != poly)
	 { ie = ie->next;
	 }
	ieStart = ie;

	do
	 { 
	   /* If the region at h is exposed, add the hide event */

	   h = ie->rIn;
	   ph = ie->edgeIn;
	   evBak = 0;
	   for (evFwd=ph->nextEvents; evFwd && evFwd->r<(h-EPSILON); 
					      evFwd=evFwd->next)
	    { evBak = evFwd;
	    }
	   if ((!evBak && ph->visible) ||
	       (evBak && evBak->type == EXPOSE_EVENT))
	    { hide = newPevent(HIDE_EVENT,h);
	      if (evBak)
	       { evBak->next = hide;
	       }
	      else
	       { ph->nextEvents = hide;
	       }
	    }
	   else
	    { hide = evBak;
	    }

	   /* find the edge with the corresponding OUT event */

	   ie = ie->next;
	   pe = ie->edgeOut;

	   if (ph != pe)
	    { ee = ph->next;
	      ee->visible = 0;
	      evFwd = ee->nextEvents;
	      while (pe != ee)
	       { ee->nextEvents = 0;
		 ee = ee->next;
		 ee->prevEvents = 0;
		 ee->visible = 0;
		 evFwd = ee->nextEvents;
	       }
	    }

	   /* If the region at e is exposed, add the expose event */

	   e = ie->rOut;
	   for ( ; evFwd && evFwd->r<(e+EPSILON); evFwd=evFwd->next)
	      ;
	   if ((!evFwd && pe->next->visible) || 
	       (evFwd && evFwd->type == HIDE_EVENT))
	    { expose = newPevent(EXPOSE_EVENT,e);
	      expose->next = evFwd;
	      if (evFwd == NULL)
	       { pe->next->prevEvents = expose;
	       }
	    }
	   else
	    { expose = evFwd;
	    }

	   if (ph == pe)
	    { if (hide)
	       { hide->next = expose;
	       }
	      else
	       { ph->nextEvents = expose;
	       }
	      if (expose == NULL)
	       { ph->next->prevEvents = hide;
	       }
	    }
	   else
	    { if (hide)
	       { hide->next = 0;
	       }
	      else
	       { ph->nextEvents = 0;
	       }
	      ph->next->prevEvents = hide;
	      pe->nextEvents = expose;
	      if (expose == NULL)
	       { pe->next->prevEvents = 0;
	       }
	    }

	   ie = ie->next;
	 }
	while (ie != ieStart);

	/* Finally, go through the polygon and clean it up:
	   Delete any events that are very close together, and
	   count the total number of visible edges. */

	poly->visible = 0;
	ph = poly->edges;
	do
	 { if (ph->visible || ph->nextEvents)
	    { poly->visible++;
	    }
	   ph = ph->next;
	 }
	while (ph != poly->edges);

	/* sanity checks: */

	 { int visible;
	   float lastr;

	   ph = poly->edges;
	   visible = ph->visible;
	   do
	    { lastr = -1.0;
	      evBak = 0;
	      for (evFwd=ph->nextEvents; evFwd; evFwd=evFwd->next)
	       { if (lastr != -1.0 && lastr > evFwd->r)
		  { printf ("obscurePolygon: r out of order\n");
		    exit (-1);
		  }
		 if (evFwd->r < 0.0 || evFwd->r > 1.0)
		  { printf ("obscurePolygon: bad r value\n");
		    exit (-1);
		  }
		 if (evFwd->type == HIDE_EVENT)
		  { if (!visible)
		     { printf ("obscurePolygon: bad hide event\n");
		       exit (-1);
		     }
		    visible = 0;
		  }
		 else
		  { if (visible)
		     { printf ("obscurePolygon: bad expose event\n");
		       exit (-1);
		     }
		    visible = 1;
		  }
		 evBak = evFwd;
	       }	
	      if (evBak != ph->next->prevEvents)
	       { printf ("obscurePolygon: prevEvent != last event\n");
		 exit (-1);
	       }
	      if (visible != ph->next->visible)
	       { printf ("obscurePolygon: next vtx visible out of sync\n");
		 exit (-1);
	       }
	      ph = ph->next;
	    }
	   while (ph != poly->edges);
	}

}
#endif

drawPolygon(pgn)
SCR_POLYGON *pgn;
{
	PEDGE *p1, *p2;
	PEVENT *ev;
	float dx, dy;
	int visible;
	float lastExposeR;

	if (!pgn->visible)
	 { return;
	 }
	p1 = pgn->edges;
	visible = p1->visible;
	if (visible)
	 { grf4ScrMove (p1->x, p1->y);
	 }
	do
	 { p2 = p1->next;
	   lastExposeR = 0.0;
	   if (p1->nextEvents)
	    { dx = p2->x - p1->x;
	      dy = p2->y - p1->y;
	      for (ev=p1->nextEvents; ev; ev=ev->next)
	       { if (ev->type == HIDE_EVENT)
		  { grf4ScrDraw (p1->x + ev->r*dx, p1->y + ev->r*dy);
		    visible = 0;
		  }
		 else 
		  { grf4ScrMove (p1->x + ev->r*dx, p1->y + ev->r*dy);
		    lastExposeR = ev->r;
		    visible = 1;
		  }
	       }
	    }
	   if (visible && lastExposeR != 1.0)
	    { grf4ScrDraw (p2->x, p2->y);
	    }
	   p1 = p2;
	 }
	while (p1 != pgn->edges);
}


/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 
			Stuff for writing PostScript
   -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */

setPsCom(com, x, y)
int com;
float x, y;
{
	static PsComBlk *pcb;
	PsCom *pc;

	if (x < psMinX)
	 { psMinX = x;
	 }
	if (x > psMaxX)
	 { psMaxX = x;
	 }
	if (y < psMinY)
	 { psMinY = y;
	 }
	if (y > psMaxY)
	 { psMaxY = y;
	 }

	if (pcbList == 0)
	 { pcb = ALLOC(PsComBlk);
	   pcb->num = 0;
	   pcb->next = 0;
	   pcbList = pcbTail = pcb;
	 }
	else if (pcb->num >= PCB_SIZE)
	 { pcb = ALLOC(PsComBlk);
	   pcb->num = 0;
	   pcb->next = 0;
	   pcbTail->next = pcb;
	   pcbTail = pcb;
	 }
	pc = &pcbTail->buf[pcb->num++];
	pc->com = com;
	pc->x = x;
	pc->y = y;
}

flushPsComs()
{
	PsComBlk *pcb, *pcb_next;
	int k;
	
	for (pcb=pcbList; pcb; pcb=pcb_next)
	 { pcb_next = pcb->next;
	   for (k=0; k<pcb->num; k++)
	    { switch (pcb->buf[k].com)
	       { case PS_MOVE:
		  { fprintf (psFp, "%g %g moveto\n", pcb->buf[k].x, 
						     pcb->buf[k].y);
		    break;
		  }
		 case PS_DRAW:
		  { fprintf (psFp, "%g %g lineto\n", pcb->buf[k].x, 
						     pcb->buf[k].y);
		    break;
		  }
	       }
	    }
	   free ((char*)pcb);
	 }
	pcbList = 0;
}

PostScriptInit (fp, name, flags, width, height)
FILE *fp;
char *name;
int flags;
float width, height; /* in points */
{
	/* set up the initial transform and the linewidth */

	/* adjust width or height for the appropriate aspect ratio */

	if (winh/(double)winw > height/width)
	 { /* screen is taller and thinner than desired; reduce width */
	   width = height*(winw/(double)winh);
	 }
	else
	 { /* screen is shorter and fatter than desired; reduce height */
	   height = width*(winh/(double)winw);
	 }

	/* for now, set the scaling parameters to put the image in the
	   middle of an 8.5" by 11" screen. */

	psXm = (width-1)/(virtR-virtL);
	psXb = 0.5-virtL*psXm + (72*8.5-width)/2;
	psYm = (height-1)/(virtT-virtB);
	psYb = 0.5-virtB*psYm + (72*11-height)/2;
	psFp = fp;
	if (name)
	 { psName = strcpy((char*)malloc(strlen(name)+1),name);
	 }
	else
	 { psName = 0;
	 }
	psFlags = flags;

	if (flags & GRF_IDRAW_PS)
	 { IdrawInit();
	 }
	psMaxX = psMaxY = -1e20;
	psMinX = psMinY =  1e20;
}

PostScriptClose()
{
	if (psFlags & GRF_IDRAW_PS)
	 { IdrawFlush();
	   IdrawClose();
	 }
	else
	 { fprintf (psFp, "%%!PS-Adobe-2.0\n");
	   fprintf (psFp, "%%%%Creator: robotsim\n");
	   fprintf (psFp, "%%%%BoundingBox: %9.4f %9.4f %9.4f %9.4f\n",
			   psMinX-1, psMinY-1, psMaxX+1, psMaxY+1);
	   fprintf (psFp, "1 setlinewidth\n");
	   fprintf (psFp, "1 setlinecap\n");
	   fprintf (psFp, "1 setlinejoin\n");
	   fprintf (psFp, "newpath\n");
	   flushPsComs();
	   fprintf (psFp, "stroke\n");
	   fprintf (psFp, "showpage\n");
	 }
	if (psName)
	 { free (psName);
	 }
	psFp = 0;
}

PostScriptMove (x, y)
register float x, y;
{
	if (psFlags & GRF_IDRAW_PS)
	 { IdrawMove (x*psXm+psXb, y*psYm+psYb);
	 }
	else
	 { setPsCom (PS_MOVE, x*psXm+psXb, y*psYm+psYb);
	 }
}

PostScriptDraw (x, y)
register float x, y;
{
	if (psFlags & GRF_IDRAW_PS)
	 { IdrawDraw (x*psXm+psXb, y*psYm+psYb);
	 }
	else
	 { setPsCom (PS_DRAW, x*psXm+psXb, y*psYm+psYb);
	 }
}

PostScriptVector (v)
VECTOR *v;
{
	if (v->events == NULL)
	 { if (v->visible)
	    { PostScriptMove (v->x1,v->y1);
	      PostScriptDraw (v->x2,v->y2);
	    }
	 }
	else
	 { float dx, dy;
	   PEVENT *ev, *ep;

	   dx = v->x2 - v->x1;
	   dy = v->y2 - v->y1;
	   ep = NULL;
	   for (ev=v->events; ev; ev=ev->next)
	    { if (ev->type == HIDE_EVENT && ev->r != 0.0)
	       { if (ep)
		  { PostScriptMove (v->x1 + ep->r*dx, v->y1 + ep->r*dy);
		  }
		 else
		  { PostScriptMove (v->x1, v->y1);
		  }
		 PostScriptDraw (v->x1 + ev->r*dx, v->y1 + ev->r*dy);
	       }
	      else if (ev->type == EXPOSE_EVENT && ev->next == NULL &&
		       ev->r != 1.0)
	       { 
		 PostScriptMove (v->x1 + ev->r*dx, v->y1 + ev->r*dy);
		 PostScriptDraw (v->x2, v->y2);
	       }
	      ep = ev;
	    }
	 }
}
	
PostScriptPolygon (pgn)
SCR_POLYGON *pgn;
{
	PEDGE *p1, *p2;
	PEVENT *ev;
	float dx, dy;
	int visible;
	float lastExposeR;

	if (!pgn->visible)
	 { return;
	 }
	p1 = pgn->edges;
	visible = p1->visible;
	if (visible)
	 { PostScriptMove (p1->x, p1->y);
	 }
	do
	 { p2 = p1->next;
	   lastExposeR = 0.0;
	   if (p1->nextEvents)
	    { dx = p2->x - p1->x;
	      dy = p2->y - p1->y;
	      for (ev=p1->nextEvents; ev; ev=ev->next)
	       { if (ev->type == HIDE_EVENT)
		  { PostScriptDraw (p1->x + ev->r*dx, p1->y + ev->r*dy);
		    visible = 0;
		  }
		 else 
		  { PostScriptMove (p1->x + ev->r*dx, p1->y + ev->r*dy);
		    lastExposeR = ev->r;
		    visible = 1;
		  }
	       }
	    }
	   if (visible && lastExposeR != 1.0)
	    { PostScriptDraw (p2->x, p2->y);
	    }
	   p1 = p2;
	 }
	while (p1 != pgn->edges);
}

#define MAXPNTS 1024

struct {
  float x, y; } ppnts[MAXPNTS];
int numPnts;

IdrawMove(x, y)
float x, y;
{
	IdrawFlush();
	ppnts[0].x = x;
	ppnts[0].y = y;
	numPnts = 1;
}

IdrawDraw(x, y)
float x, y;
{
	static int warned = 0;

	if (numPnts == MAXPNTS)
	 { if (!warned)
	    { printf ("IdrawDraw(): Number of draw points > %d\n", MAXPNTS);
	      warned = 1;
	    }
	   return;
	 }
	ppnts[numPnts].x = x;
	ppnts[numPnts].y = y;
	numPnts++;
}

IdrawFlush()
{
	int k;

	if (numPnts > 1)
	 { fprintf (psFp, "\nBegin %%I MLine\n");
	   fprintf (psFp, "%%I b 65535\n");
	   fprintf (psFp, "1 0 0 [] 0 SetB\n");
	   fprintf (psFp, "%%I cfg Black\n");
	   fprintf (psFp, "0 0 0 SetCFg\n");
	   fprintf (psFp, "%%I cbg White\n");
	   fprintf (psFp, "1 1 1 SetCBg\n");
	   fprintf (psFp, "none SetP %%I p n\n");
	   fprintf (psFp, "%%I t\n");
	   fprintf (psFp, "[ .01 0 0 .01 0 0 ] concat\n");
	   fprintf (psFp, "%%I %d\n", numPnts);
	   for (k=0; k<numPnts; k++)
	    { fprintf (psFp, "%d %d\n", (int)(100*ppnts[k].x),
					(int)(100*ppnts[k].y) );
	    }
	   fprintf (psFp, "%d MLine\n", numPnts);
	   fprintf (psFp, "End\n");
	 }
	numPnts = 0;
}

IdrawInit()
{
	fprintf (psFp, "%%I Idraw 9 Grid 8\n\n");
	fprintf (psFp, "%%%%Page: 1 1\n\n");
	fprintf (psFp, "Begin\n");
	fprintf (psFp, "%%I b u\n");
	fprintf (psFp, "%%I cfg u\n");
	fprintf (psFp, "%%I cbg u\n");
	fprintf (psFp, "%%I f u\n");
	fprintf (psFp, "%%I p u\n");
	fprintf (psFp, "%%I t u\n");
	fprintf (psFp, "/originalCTM matrix currentmatrix def\n");
}

IdrawClose()
{
	fprintf (psFp, "\nEnd %%I eop\n\n");
}

#endif
