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

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

extern TRSF* grf4TransPtr;
extern TRSF grf4IdentTrsf;
extern int grf4PerspectiveMode;
extern float grf4NearPlane;

#if HLS
extern int grf4HiddenLineMode;
#endif

extern grf4ScrMove();
extern grf4ScrDraw();

#define SMALL (1e-7)

static float *crossV3f ();
static float dotV3f ();
static float *normalizeV3f ();
static float *multTrsfV3f();
static float *multTrsfXy();
static float *multRotV3f();
static void buildNormals();

static float *crossV3f (vr, v1, v2)
float *vr, *v1, *v2;
{
	vr[0] = v1[1]*v2[2] - v1[2]*v2[1];
	vr[1] = v1[2]*v2[0] - v1[0]*v2[2];
	vr[2] = v1[0]*v2[1] - v1[1]*v2[0];
	return(vr);
}

static float dotV3f (v1, v2)
float *v1, *v2;
{
	return (v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]);
}

static float *normalizeV3f (vr, v1)
float *vr, *v1;
{
	float mag;

	mag = sqrt(v1[0]*v1[0] + v1[1]*v1[1] + v1[2]*v1[2]);
	vr[0] = v1[0]/mag;
	vr[1] = v1[1]/mag;
	vr[2] = v1[2]/mag;
	return(vr);
}

static float *multTrsfV3f(vr, t1, v1)
float *vr, *v1;
register TRSF *t1;
{
	register float x, y, z;

	x = v1[0];
	y = v1[1];
	z = v1[2];
	
	vr[0] = t1->n.x*x + t1->o.x*y + t1->a.x*z + t1->p.x;
	vr[1] = t1->n.y*x + t1->o.y*y + t1->a.y*z + t1->p.y;
	vr[2] = t1->n.z*x + t1->o.z*y + t1->a.z*z + t1->p.z;
}
	
static float *multTrsfXy(vr, t1, x, y)
float *vr;
register TRSF *t1;
register float x, y;
{
	vr[0] = t1->n.x*x + t1->o.x*y + t1->p.x;
	vr[1] = t1->n.y*x + t1->o.y*y + t1->p.y;
	vr[2] = t1->n.z*x + t1->o.z*y + t1->p.z;
}
	
static float *multRotV3f(vr, t1, v1)
float *vr, *v1;
register TRSF *t1;
{
	register float x, y, z;

	x = v1[0];
	y = v1[1];
	z = v1[2];
	
	vr[0] = t1->n.x*x + t1->o.x*y + t1->a.x*z;
	vr[1] = t1->n.y*x + t1->o.y*y + t1->a.y*z;
	vr[2] = t1->n.z*x + t1->o.z*y + t1->a.z*z;
}
	
#define NEXT_IDX(k,num)	(((k)==((num)-1)) ? 0 : (k)+1)
#define PREV_IDX(k,num)	((k)==0 ? ((num)-1) : (k)-1)

static void buildNormals(cylin)
PRISM *cylin;
{
	CEDGE *ce;
	float v1[3], v2[3];
	int k, n;
	float *nrmls;
	float *fnk;

	ce = cylin->edges;

	nrmls = (float*)malloc(2*sizeof(float)*3*3*cylin->size);
	
	for (k=0; k<cylin->size; k++)
	 { n = NEXT_IDX(k,cylin->size);

	   v1[0] = ce[n].x_b - ce[k].x_b;
	   v1[1] = ce[n].y_b - ce[k].y_b;
	   v1[2] = 0.0;

	   v2[0] = ce[k].x_t - ce[k].x_b;
	   v2[1] = ce[k].y_t - ce[k].y_b;
	   v2[2] = cylin->depth;

	   fnk = nrmls; 
	   nrmls += 3;
	   normalizeV3f (fnk, crossV3f(fnk, v1, v2));
	   ce[k].faceNrml = fnk;
	   ce[k].eNrml = nrmls;
	   nrmls += 3;
	 }
}

PRISM *allocPrism(npts)
int npts;
{
	PRISM *c;
	CEDGE *ep;
	int k;

	c = ALLOC(PRISM);
	bzero ((char*)c, sizeof(PRISM));
	ep = (CEDGE*)malloc(sizeof(CEDGE)*npts);
	for (k=0; k<npts; k++)
	 { ep[k].next = ((k < npts-1) ? &ep[k+1] : &ep[0]);
	   ep[k].prev = ((k > 0) ? &ep[k-1] : &ep[npts-1]);
	 }
	c->edges = ep;
	return (c);
}

PRISM *gdrGenDisc(npts, radius, depth)
int npts;
float radius;
float depth;
{
	PRISM *c;
	CEDGE *ep;
	float v1[3], v2[3];
	float *nrmls, *fnk;
	int n, k;

	c = allocPrism(npts);
	for (k=0, ep=c->edges; k<npts; k++, ep++)
	 { ep->x_b = radius * cos(k*2*PI/npts);
	   ep->y_b = radius * sin(k*2*PI/npts);
	   ep->x_t = ep->x_b;
	   ep->y_t = ep->y_b;
	   ep->hard = 0;
	 }
	c->depth = depth;
	c->size = npts;
	c->parallel = YES;
	buildNormals (c);
	return(c);
}

PRISM *gdrGenCone(npts, radius1, radius2, depth)
int npts;
float radius1;
float radius2;
float depth;
{
	PRISM *c;
	CEDGE *ep;
	int k;

	c = allocPrism(npts);
	for (k=0, ep=c->edges; k<npts; k++, ep++)
	 { ep->x_b = radius1 * cos(k*2*PI/npts);
	   ep->y_b = radius1 * sin(k*2*PI/npts);
	   ep->x_t = radius2 * cos(k*2*PI/npts);
	   ep->y_t = radius2 * sin(k*2*PI/npts);
	   ep->hard = 0;
	 }
	c->depth = depth;
	c->size = npts;
	c->parallel = NO;
	buildNormals (c);
	return(c);
}

PRISM *gdrGenPrism (npts, bot, top, hidden, depth)
int npts;
float *bot, *top;
char *hidden;
float depth;
{
	PRISM *c;
	CEDGE *ep;
	char *hp;
	int k;

	c = allocPrism(npts);
	for (k=0, ep=c->edges, hp=hidden; k<npts; k++, ep++)
	 { ep->x_b = bot[2*k+0];
	   ep->y_b = bot[2*k+1];
	   if (top)
	    { ep->x_t = top[2*k+0];
	      ep->y_t = top[2*k+1];
	    }
	   else
	    { ep->x_t = bot[2*k+0];
	      ep->y_t = bot[2*k+1];
	    }
	   if (hp && *hp != '\0')
	    { ep->hard = (*hp++ == '1' ? 0 : 1);
	    }
	   else
	    { ep->hard = 1;
	    }
	 }
	c->depth = depth;
	c->size = npts;
	c->parallel = (top == 0);
	buildNormals (c);
	return(c);
}

SPHERE *gdrGenSphere (npts, radius)
int npts;
float radius;
{
	SPHERE *s;

	s = ALLOC(SPHERE);
	bzero((char*)s, sizeof(SPHERE));
	s->radius = radius;
	s->npts = npts;
	return(s);
}

POLYGON *gdrGenPolygon (npts, vtxs)
int npts;
float *vtxs;
{
	POLYGON *p;
	int k;

	p = ALLOC(POLYGON);
	bzero((char*)p, sizeof(POLYGON));
	p->edges = (float*)malloc(2*sizeof(float)*npts);
	p->size = npts;
	for (k=0; k<2*npts; k++)
	 { p->edges[k] = vtxs[k];
	 }
	return (p);
}

POLYGON *gdrGenRegPolygon (npts, size)
int npts;
float size;
{
	POLYGON *p;
	double r, theta;
	int k;

	if (npts < 3)
	 { return (0);
	 }
	p = ALLOC(POLYGON);
	bzero((char*)p, sizeof(POLYGON));
	p->edges = (float*)malloc(2*sizeof(float)*npts);
	p->size = npts;
	r = size/(2*sin(PI/npts));
	for (k=npts-1; k>=0; k--)
	 { p->edges[2*k]   = r*cos(k*2*PI/npts);
	   p->edges[2*k+1] = r*sin(k*2*PI/npts);
	 }
	return (p);
}

LINESET *gdrGenLineSet (num, lines)
int num;
float *lines;
{
	LINESET *l;
	int k;

	l = ALLOC(LINESET);
	bzero((char*)l, sizeof(LINESET));
	l->lines = (float*)malloc(4*sizeof(float)*num);
	l->num = num;
	for (k=0; k<4*num; k++)
	 { l->lines[k] = lines[k];
	 }
	return (l);
}

double gdrSetDepth (obj, depth)
Grf4Object *obj;
float depth;
{
	float old;

	old = obj->ptr.prism->depth;
	obj->ptr.prism->depth = depth;
	return (old);
}

SCR_POLYGON *theFace;

void gdrDrawPrism (obj)
Grf4Object *obj;
{
	PRISM *cy = obj->ptr.prism;

	register TRSF *T;
	register CEDGE *ep, *np;
	register float x, y;

	float dex, dey, dez;
	float pterm;
	float sx_b_next, sy_b_next;
	float ex_b, ey_b, ez_b;
	float ex_t, ey_t, ez_t;

	float vpnt[3];

	CEDGE *visBegin, *visEnd;

	if (cy->size < 2)
	 { fprintf (stderr, "drawPrism: less than 2 points\n");
	   exit (-1);
	 }

	T = (grf4TransPtr ? grf4TransPtr : &grf4IdentTrsf);
	
	/* compute eye coordinates between bottom and top */

	dex = T->a.x * cy->depth;
	dey = T->a.y * cy->depth;
	dez = T->a.z * cy->depth;

	/* Decide which face of the prism to use */
	if (!grf4PerspectiveMode)
	 { cy->bottomVisible = (T->a.z < -SMALL);
	   cy->topVisible    = (T->a.z > SMALL);
	 }
	else
	 { float w;
	   w = T->p.x*T->a.x + T->p.y*T->a.y + T->p.z*T->a.z;
	   cy->bottomVisible = (w > SMALL);
	   cy->topVisible = (w + dex*T->a.x+dey*T->a.y+dez*T->a.z <= -SMALL);
	 }

	/* run around and compute visibility and eye coordinates */

	ep = cy->edges;				
	do 
	 { multRotV3f (ep->eNrml, T, ep->faceNrml);
	   vpnt[0] = ep->x_b;
	   vpnt[1] = ep->y_b;
	   vpnt[2] = 0.0;
	   multTrsfV3f (vpnt, T, vpnt);

	   ep->ex_b = vpnt[0];
	   ep->ey_b = vpnt[1];
	   ep->ez_b = vpnt[2];

	   if (cy->parallel)
	    { ep->ex_t = ep->ex_b + dex;
	      ep->ey_t = ep->ey_b + dey;
	      ep->ez_t = ep->ez_b + dez;
	    }
	   else
	    { x = ep->x_t;
	      y = ep->y_t;
	      ep->ex_t = T->n.x*x + T->o.x*y + T->p.x + dex;
	      ep->ey_t = T->n.y*x + T->o.y*y + T->p.y + dey;
	      ep->ez_t = T->n.z*x + T->o.z*y + T->p.z + dez;
	    }

	   if (!grf4PerspectiveMode)
	    { ep->visible = (ep->eNrml[2] > SMALL);
	    }
	   else
	    { ep->visible = (dotV3f (vpnt, ep->eNrml) < -SMALL);
	    }
	   ep = ep->next;
	 }
	while (ep != cy->edges);

#if HLS
	if (grf4HiddenLineMode)
	 {
	   np = ep->next;
	   do
	    { if (ep->visible)
	       { gdrBeginPolygon((void*)cy);
		 gdrNrml (ep->eNrml[0], ep->eNrml[1], ep->eNrml[2]);
		 gdrVrtx (ep->ex_t, ep->ey_t, ep->ez_t, 
			  ep->hard || !ep->prev->visible);
		 gdrVrtx (ep->ex_b, ep->ey_b, ep->ez_b, 1);
		 gdrVrtx (np->ex_b, np->ey_b, np->ez_b,
			  np->hard || !np->visible);
		 gdrVrtx (np->ex_t, np->ey_t, np->ez_t,1);
		 gdrEndPolygon();
	       }
	      ep = np;
	      np = ep->next;
	    }
	   while (ep != cy->edges);

	   if (cy->topVisible)
	    { theFace = (SCR_POLYGON*)gdrBeginPolygon((void*)cy);
	      gdrNrml (T->a.x, T->a.y, T->a.z);
	      do
	       { gdrVrtx (ep->ex_t, ep->ey_t, ep->ez_t, 1);
		 ep = ep->next;
	       }
	      while (ep != cy->edges);
	      gdrEndPolygon();
	    }
	   else if (cy->bottomVisible)
	    { theFace = (SCR_POLYGON*)gdrBeginPolygon((void*)cy);
	      gdrNrml (-T->a.x, -T->a.y, -T->a.z);
	      do
	       { gdrVrtx (ep->ex_b, ep->ey_b, ep->ez_b, 1);
		 ep = ep->prev;
	       }
	      while (ep != cy->edges);
	      gdrEndPolygon();
	    }
	 }
	else
#endif
	 { 
	   np = ep->next;
	   do
	    { if (ep->visible)
	       { grf4DrawVector (ep->ex_b, ep->ey_b, ep->ez_b,
			     np->ex_b, np->ey_b, np->ez_b);
		 grf4DrawVector (ep->ex_t, ep->ey_t, ep->ez_t,
			     np->ex_t, np->ey_t, np->ez_t);
		 if (ep->hard || !ep->prev->visible)
		  { grf4DrawVector (ep->ex_b, ep->ey_b, ep->ez_b,
				ep->ex_t, ep->ey_t, ep->ez_t);
		  }
		 if (!np->visible)
		  { grf4DrawVector (np->ex_b, np->ey_b, np->ez_b,
				np->ex_t, np->ey_t, np->ez_t);
		  }
	       }
	      else if (cy->topVisible)
	       { grf4DrawVector (ep->ex_t, ep->ey_t, ep->ez_t,
			     np->ex_t, np->ey_t, np->ez_t);
	       }
	      else if (cy->bottomVisible)
	       { grf4DrawVector (ep->ex_b, ep->ey_b, ep->ez_b,
			     np->ex_b, np->ey_b, np->ez_b);
	       }
	      ep = np;
	      np = ep->next;
	    }
	   while (ep != cy->edges);
	 }
} /* end gdrDrawPrism() */

void gdrDrawSphere (obj)
Grf4Object *obj;
{
	SPHERE *sphere = obj->ptr.sphere;

#define NUM_PNTS 12
	static float sinK[NUM_PNTS];
	static float cosK[NUM_PNTS];
	static initDone;
	static VECT xunit = { 1.0, 0.0, 0.0 };
	static VECT yunit = { 0.0, 1.0, 0.0 };

	TRSF *T, eye;
	float sx, sy;
	float d, pm, h, pt;
	register int k;
	float r;

	r = sphere->radius;
	T = (grf4TransPtr ? grf4TransPtr : &grf4IdentTrsf);

	/* Check for an admissible sphere. Basically, we clip the
	   sphere out if it's center passes in front of the near
	   clipping plane. It is assumed here that the near clipping
	   plane is in front of the eye. */

	identTrsf(&eye);
	eye.p = T->p;
	if (eye.p.z > grf4NearPlane)
	 { return;	/* don't draw sphere */
	 }

	if (grf4PerspectiveMode)
	 {
	   pm = sqrt(eye.p.x*eye.p.x + eye.p.y*eye.p.y + eye.p.z*eye.p.z);
	   eye.a.x = eye.p.x/pm;
	   eye.a.y = eye.p.y/pm;
	   eye.a.z = eye.p.z/pm;
	   if (FABS(eye.a.x) > FABS(eye.a.y))
	    { axesToTrsf (&eye, "zy", &eye.a, &yunit);
	    }
	   else
	    { axesToTrsf (&eye, "zx", &eye.a, &xunit);
	    }
	   h = r*sqrt(pm*pm - r*r)/pm;
	 }
	else
	 { h = r;
	 }
	if (!initDone)
	 { initDone = 1;
	   for (k=0; k<NUM_PNTS; k++)
	    { cosK[k] = cos(k*PI*2.0/NUM_PNTS);
	      sinK[k] = sin(k*PI*2.0/NUM_PNTS);
	    }
	 }
	pt = (grf4PerspectiveMode ? grf4NearPlane/(eye.n.z*h + eye.p.z) : 1.0);
	sx = (eye.n.x*h + eye.p.x)*pt;
	sy = (eye.n.y*h + eye.p.y)*pt;

#if HLS
	if (grf4HiddenLineMode)
	 { theFace = (SCR_POLYGON*)gdrBeginPolygon((void*)sphere);
	   for (k=NUM_PNTS-1; k>=0; k--)
	    { float x0, y0;
	      float ex, ey, ez;

	      x0 = h * cosK[k];
	      y0 = h * sinK[k];
	      ex = eye.n.x*x0 + eye.o.x*y0 + eye.p.x;
	      ey = eye.n.y*x0 + eye.o.y*y0 + eye.p.y;
	      ez = eye.n.z*x0 + eye.o.z*y0 + eye.p.z;
	      gdrVrtx (ex, ey, ez, 1);
	    }
	   gdrNrml (-eye.a.x, -eye.a.y, -eye.a.z);
	   gdrEndPolygon();
	 }
	else
#endif
	 { 
	   grf4ScrMove (sx, sy);

	   for (k=0; k<NUM_PNTS; k++)
	    { float x0, y0;

	      x0 = h * cosK[(k+1)%NUM_PNTS];
	      y0 = h * sinK[(k+1)%NUM_PNTS];
	      pt = (grf4PerspectiveMode ? 
		    grf4NearPlane/(eye.n.z*x0 + eye.o.z*y0 + eye.p.z) : 1.0);
	      sx = (eye.n.x*x0 + eye.o.x*y0 + eye.p.x)*pt;
	      sy = (eye.n.y*x0 + eye.o.y*y0 + eye.p.y)*pt;
	      grf4ScrDraw (sx, sy);
	    }
	 }
}

void gdrDrawPolygon (obj)
Grf4Object *obj;
{
	POLYGON *poly = obj->ptr.polygon;

	register TRSF *T;
	register float ex, ey, ez;
	float ev0[3], ev1[3], ev2[3];
	register float *vtx;
	int fwdView;
	int k;

	if (poly->size < 3)
	 { fprintf (stderr, "drawPolygon: less than 3 points\n");
	   exit (-1);
	 }

	T = (grf4TransPtr ? grf4TransPtr : &grf4IdentTrsf);
	
#if HLS
	if (grf4HiddenLineMode)
	 { if (!grf4PerspectiveMode)
	    { fwdView = (T->a.z > SMALL);
	    }
	   else
	    { fwdView = (T->p.x*T->a.x+T->p.y*T->a.y+T->p.z*T->a.z < -SMALL);
	    }
	   vtx = (fwdView ? poly->edges : poly->edges+2*(poly->size-1));
	   gdrBeginPolygon(0);
	   gdrNrml (T->a.x, T->a.y, T->a.z);
	   for (k=0; k<poly->size; k++)
	    { multTrsfXy (ev0, T, vtx[0], vtx[1]);
	      gdrVrtx (ev0[0], ev0[1], ev0[2], 1);
	      vtx = (fwdView ? vtx+2 : vtx-2);
	    }
	   gdrEndPolygon();
	 }
	else
#endif
	 { vtx = poly->edges;
	   multTrsfXy (ev0, T, vtx[0], vtx[1]);
	   ev2[0] = ev0[0];
	   ev2[1] = ev0[1];
	   ev2[2] = ev0[2];
	   for (k=1; k<poly->size; k++)
	    { ev1[0] = ev2[0];
	      ev1[1] = ev2[1];
	      ev1[2] = ev2[2];
	      vtx += 2;
	      multTrsfXy (ev2, T, vtx[0], vtx[1]);
	      grf4DrawVector (ev1[0], ev1[1], ev1[2], ev2[0], ev2[1], ev2[2]);
	    }
	   grf4DrawVector (ev2[0], ev2[1], ev2[2], ev0[0], ev0[1], ev0[2]);
	 }
}

void gdrDrawLineSet(obj)
Grf4Object *obj;
{
	LINESET *lset = obj->ptr.lineSet;

#define X1	0
#define Y1	1
#define X2	2
#define Y2	3

	register TRSF *T;
	register float *l;
	register float x, y;
	float *l_end;
	float ex1, ey1, ez1;
	float ex2, ey2, ez2;

	T = grf4TransPtr;
	l_end = lset->lines + 4*lset->num;

	for (l=lset->lines; l<l_end; l+=4)
	 { if (T)
	    { x = l[X1];
	      y = l[Y1];

	      ex1 = T->n.x*x + T->o.x*y + T->p.x;
	      ey1 = T->n.y*x + T->o.y*y + T->p.y;
	      ez1 = T->n.z*x + T->o.z*y + T->p.z;

	      x = l[X2];
	      y = l[Y2];

	      ex2 = T->n.x*x + T->o.x*y + T->p.x;
	      ey2 = T->n.y*x + T->o.y*y + T->p.y;
	      ez2 = T->n.z*x + T->o.z*y + T->p.z;
	    }
	   else
	    { ex1 = l[X1];
	      ey1 = l[Y1];
	      ez1 = 0.0;
	      ex2 = l[X2];
	      ey2 = l[Y2];
	      ez2 = 0.0;
	    }
#if HLS
	   if (grf4HiddenLineMode)
	    { gdrVector (ex1, ey1, ez1, ex2, ey2, ez2);
	    }
	   else
#endif
	    { 
	      grf4DrawVector (ex1, ey1, ez1, ex2, ey2, ez2);
	    }
	 }
}

float area(dx1,dy1,dx2,dy2)
float dx1,dy1,dx2,dy2;
{
	return (dx1*dy2 - dy1*dx2);
}

#if 0
int gdrOverPrism(depth, cy, x, y)
float *depth;
PRISM *cy;
register float x, y;
{
	return(0);
}


int gdrOverSphere(depth, s, x, y)
float *depth;
SPHERE *s;
register float x, y;
{
	return(0);
}
#endif

#if 1

int gdrOverPrism(depth, obj, x, y)
float *depth;
Grf4Object *obj;
register float x, y;
{
	PRISM *cy = obj->ptr.prism;

	/* XXX Check bounding box XXX */

	register float lastDx, lastDy;
	register float dx, dy;
	register int k;
	int n, p;
	float x1, x2, y1, y2;
	float nrml[3];
	register CEDGE *ep;

	ep = cy->edges;

	for (k=0; k<cy->size; k++)
	 { n = NEXT_IDX(k,cy->size);

	   if (ep[k].visible)
	    { dx = ep[n].sx_b - x;
	      dy = ep[n].sy_b - y;
	      if (area (ep[k].sx_b-x, ep[k].sy_b-y, dx, dy) < 0.0)
	       { continue;
	       }
	      lastDx = dx;
	      lastDy = dy;
	      dx = ep[n].sx_t - x;
	      dy = ep[n].sy_t - y;
	      if (area (lastDx, lastDy, dx, dy) < 0.0)
	       { continue;
	       }
	      lastDx = dx;
	      lastDy = dy;
	      dx = ep[k].sx_t - x;
	      dy = ep[k].sy_t - y;
	      if (area (lastDx, lastDy, dx, dy) < 0.0)
	       { continue;
	       }
	      lastDx = dx;
	      lastDy = dy;
	      dx = ep[k].sx_b - x;
	      dy = ep[k].sy_b - y;
	      if (area (lastDx, lastDy, dx, dy) < 0.0)
	       { continue;
	       }
	      /* we got here, which means the point is inside this
		 edge face. */
	      multRotV3f (nrml, obj->trsf, ep[k].faceNrml);
	      return (computeDepth (depth, nrml, 
		   		    ep[k].sx_b, ep[k].sy_b, ep[k].ez_b, x, y));
	    }
	 }
	if (cy->topVisible)
	 { lastDx = ep[cy->size-1].sx_t - x;
	   lastDy = ep[cy->size-1].sy_t - y;
	   for (k=0; k<cy->size; k++)
	    { dx = ep[k].sx_t-x;
	      dy = ep[k].sy_t-y;
	      if (area (lastDx, lastDy, dx, dy) < 0.0)
	       { return (0);
	       }
	      lastDx = dx;
	      lastDy = dy;
	    }
	   /* Got here, so we are inside the top face */
	   nrml[0] = obj->trsf.a.x;
	   nrml[1] = obj->trsf.a.y;
	   nrml[2] = obj->trsf.a.z;
	   return (computeDepth (depth, nrml, 
				 ep[0].sx_t, ep[0].sy_t, ep[0].ez_t, x, y));
	 }
	else if (cy->bottomVisible)
	 { lastDx = ep[cy->size-1].sx_b - x;
	   lastDy = ep[cy->size-1].sy_b - y;
	   for (k=0; k<cy->size; k++)
	    { dx = ep[k].sx_b-x;
	      dy = ep[k].sy_b-y;
	      if (area (lastDx, lastDy, dx, dy) > 0.0)
	       { return (0);
	       }
	      lastDx = dx;
	      lastDy = dy;
	    }
	   /* Got here, so we are inside the bottom face */
	   nrml[0] = -obj->trsf.a.x;
	   nrml[1] = -obj->trsf.a.y;
	   nrml[2] = -obj->trsf.a.z;
	   return (computeDepth (depth, nrml, 
				 ep[0].sx_b, ep[0].sy_b, ep[0].ez_b, x, y));
	 }
	return (0);
}

int gdrOverSphere(depth, obj, x, y)
float *depth;
Grf4Object *obj;
register float x, y;
{
	SPHERE *s = obj->ptr.sphere;
}

computeDepth (ez, n, sx0, sy0, ez0, sx, sy)
float *ez;
float *n;
float sx0, sy0, ez0, sx, sy;
{
	float denom;

	if (grf4PerspectiveMode)
	 { denom = (n[0]*sx + n[1]*sy + n[2]*grf4NearPlane);
	   if (ABS(denom) < SMALL)
	    { return (0);
	    }
	   *ez = ez0*(n[0]*sx0 + n[1]*sy0 + n[2]*grf4NearPlane)/denom;
	 }
	else
	 { if (ABS(n[2]) < SMALL)
	    { return (0);
	    }
	   *ez = (n[0]*(sx0-sx) + n[1]*(sy0-sy))/n[2] + ez0;
	 }
	return (1);
}

#endif
