/* clip.c */

/*
 * Mesa 3-D graphics library
 * Version:  1.2
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
 * Functions for clipping points, lines, and polygons against the view
 * volume and user-defined clipping planes.
 */


/*
$Id: clip.c,v 1.16 1995/12/30 17:13:05 brianp Exp $

$Log: clip.c,v $
 * Revision 1.16  1995/12/30  17:13:05  brianp
 * interpolate texture R,Q components per Bill Triggs
 *
 * Revision 1.15  1995/12/20  15:25:31  brianp
 * changed VB color indexes to GLuint
 *
 * Revision 1.14  1995/10/14  17:41:29  brianp
 * made glClipPlane display list-able
 *
 * Revision 1.13  1995/09/15  18:54:09  brianp
 * changed INSIDE, OUTSIDE macros to fix Oleg Krivosheev's clipping problems
 *
 * Revision 1.12  1995/07/28  21:32:12  brianp
 * fixed aux data interpolation bug in gl_viewclip_line()
 *
 * Revision 1.11  1995/07/24  20:34:16  brianp
 * replaced memset() with MEMSET() and memcpy() with MEMCPY()
 *
 * Revision 1.10  1995/07/14  16:11:26  brianp
 * tests for divide by zero added to gl_userclip_line()
 *
 * Revision 1.9  1995/06/02  13:59:16  brianp
 * changed MAX_VERTICES to VB_SIZE
 *
 * Revision 1.8  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.7  1995/05/12  19:24:13  brianp
 * replaced CC.Mode!=0 with INSIDE_BEGIN_END
 *
 * Revision 1.6  1995/03/24  15:30:41  brianp
 * introduced VB
 *
 * Revision 1.5  1995/03/09  21:41:03  brianp
 * new ModelViewInv matrix logic
 *
 * Revision 1.4  1995/03/09  20:07:46  brianp
 * changed order of arguments in gl_transform_vector
 *
 * Revision 1.3  1995/03/08  19:01:11  brianp
 * removed extra #include "clip.h"
 *
 * Revision 1.2  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.1  1995/02/24  14:18:09  brianp
 * Initial revision
 *
 */


#include <string.h>
#include "GL/gl.h"
#include "clip.h"
#include "context.h"
#include "list.h"
#include "macros.h"
#include "vb.h"
#include "xform.h"




/* Linear interpolation between A and B: */
#define LINTERP( T, A, B )   ( (A) + (T) * ( (B) - (A) ) )


#define EYE_SPACE 1
#define CLIP_SPACE 2

static GLuint Space;



/*
 * This function is used to interpolate colors, indexes, and texture
 * coordinates when clipping has to be done.  In general, we compute
 *     aux[dst] = aux[in] + t * (aux[out] - aux[in])
 * where aux is the quantity to be interpolated.
 * Input:  dst - index of array position to store interpolated value
 *         t - a value in [0,1]
 *         in - index of array position corresponding to 'inside' vertex
 *         out - index of array position corresponding to 'outside' vertex
 */
static void interpolate_aux( GLuint dst, GLfloat t, GLuint in, GLuint out )
{
   if (CC.ClipMask & CLIP_FCOLOR_BIT) {
      VB.Fcolor[dst][0] = LINTERP( t, VB.Fcolor[in][0], VB.Fcolor[out][0] );
      VB.Fcolor[dst][1] = LINTERP( t, VB.Fcolor[in][1], VB.Fcolor[out][1] );
      VB.Fcolor[dst][2] = LINTERP( t, VB.Fcolor[in][2], VB.Fcolor[out][2] );
      VB.Fcolor[dst][3] = LINTERP( t, VB.Fcolor[in][3], VB.Fcolor[out][3] );
   }
   else if (CC.ClipMask & CLIP_FINDEX_BIT) {
      VB.Findex[dst] = (GLuint) (GLint) LINTERP( t, (GLfloat) VB.Findex[in],
                                                 (GLfloat) VB.Findex[out] );
   }

   if (CC.ClipMask & CLIP_BCOLOR_BIT) {
      VB.Bcolor[dst][0] = LINTERP( t, VB.Bcolor[in][0], VB.Bcolor[out][0] );
      VB.Bcolor[dst][1] = LINTERP( t, VB.Bcolor[in][1], VB.Bcolor[out][1] );
      VB.Bcolor[dst][2] = LINTERP( t, VB.Bcolor[in][2], VB.Bcolor[out][2] );
      VB.Bcolor[dst][3] = LINTERP( t, VB.Bcolor[in][3], VB.Bcolor[out][3] );
   }
   else if (CC.ClipMask & CLIP_BINDEX_BIT) {
      VB.Bindex[dst] = (GLuint) (GLint) LINTERP( t, (GLfloat) VB.Bindex[in],
                                                 (GLfloat) VB.Bindex[out] );
   }

   if (CC.ClipMask & CLIP_TEXTURE_BIT) {
      /* TODO: is more sophisticated texture coord interpolation needed?? */
      if (Space==CLIP_SPACE) {
	 /* also interpolate eye Z component */
	 VB.Eye[dst][2] = LINTERP( t, VB.Eye[in][2], VB.Eye[out][2] );
      }
      VB.TexCoord[dst][0] = LINTERP(t,VB.TexCoord[in][0],VB.TexCoord[out][0]);
      VB.TexCoord[dst][1] = LINTERP(t,VB.TexCoord[in][1],VB.TexCoord[out][1]);
      VB.TexCoord[dst][2] = LINTERP(t,VB.TexCoord[in][2],VB.TexCoord[out][2]);
      VB.TexCoord[dst][3] = LINTERP(t,VB.TexCoord[in][3],VB.TexCoord[out][3]);
   }

}




void gl_clipplane( GLenum plane, const float *equation )
{
   GLint p;

   p = (GLint) (plane - GL_CLIP_PLANE0);
   if (p<0 || p>=MAX_CLIP_PLANES) {
      gl_error( GL_INVALID_ENUM, "glClipPlane" );
      return;
   }

   /*
    * The Equation is transformed by the transpose of the inverse of the
    * current modelview matrix and stored in the resulting eye coordinates.
    */
   if (!CC.ModelViewInvValid) {
      gl_compute_modelview_inverse();
   }
   gl_transform_vector( CC.Transform.ClipEquation[p], equation,
		        CC.ModelViewInv );
}



void glClipPlane( GLenum plane, const GLdouble *equation )
{
   GLfloat fequation[4];
   fequation[0] = (GLfloat) equation[0];
   fequation[1] = (GLfloat) equation[1];
   fequation[2] = (GLfloat) equation[2];
   fequation[3] = (GLfloat) equation[3];
   if (CC.CompileFlag) {
      gl_save_clipplane( plane, fequation );
   }
   if (CC.ExecuteFlag) {
      gl_clipplane( plane, fequation );
   }
}



void glGetClipPlane( GLenum plane, GLdouble *equation )
{
   GLint p;

   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glGetClipPlane" );
      return;
   }

   p = (GLint) (plane - GL_CLIP_PLANE0);
   if (p<0 || p>=MAX_CLIP_PLANES) {
      gl_error( GL_INVALID_ENUM, "glGetClipPlane" );
      return;
   }

   equation[0] = (GLdouble) CC.Transform.ClipEquation[p][0];
   equation[1] = (GLdouble) CC.Transform.ClipEquation[p][1];
   equation[2] = (GLdouble) CC.Transform.ClipEquation[p][2];
   equation[3] = (GLdouble) CC.Transform.ClipEquation[p][3];
}




/**********************************************************************/
/*                         View volume clipping.                      */
/**********************************************************************/


/*
 * Clip a point against the view volume.
 * Input:  v - vertex-vector describing the point to clip
 * Return:  0 = outside view volume
 *          1 = inside view volume
 */
GLuint gl_viewclip_point( const GLfloat v[] )
{
   if (   v[0] > v[3] || v[0] < -v[3]
       || v[1] > v[3] || v[1] < -v[3]
       || v[2] > v[3] || v[2] < -v[3] ) {
      return 0;
   }
   else {
      return 1;
   }
}




/*
 * Clip a line segment against the view volume defined by -w<=x,y,z<=w.
 * Input:  i, j - indexes into VB.V* of endpoints of the line
 * Return:  0 = line completely outside of view
 *          1 = line is inside view.
 */
GLuint gl_viewclip_line( GLuint *i, GLuint *j )
{
   GLfloat t, dx, dy, dz, dw;
   register GLuint ii, jj;

   Space = CLIP_SPACE;
   ii = *i;
   jj = *j;

/*
 * We use 6 instances of this code to clip agains the 6 planes.
 * For each plane, we define the OUTSIDE and COMPUTE_INTERSECTION
 * macros apprpriately.
 */
#define GENERAL_CLIP							\
   if (OUTSIDE(ii)) {           	                        	\
      if (OUTSIDE(jj)) {                	                	\
         /* both verts are outside ==> return 0 */			\
         return 0;                                      		\
      }                                                 		\
      else {                                            		\
         /* ii is outside, jj is inside ==> clip */     		\
	 /* new vertex put in position VB.Free */			\
         COMPUTE_INTERSECTION( VB.Free, jj, ii )             		\
	 if (CC.ClipMask)  interpolate_aux( VB.Free, t, jj, ii );	\
	 ii = VB.Free;							\
	 VB.Free++;							\
	 if (VB.Free==VB_SIZE)  VB.Free = 1;				\
      }                                                 		\
   }                                                    		\
   else {                                               		\
      if (OUTSIDE(jj)) {                                		\
         /* ii is inside, jj is outside ==> clip */     		\
	 /* new vertex put in position VB.Free */			\
         COMPUTE_INTERSECTION( VB.Free, ii, jj );            		\
	 if (CC.ClipMask)  interpolate_aux( VB.Free, t, ii, jj );	\
	 jj = VB.Free;							\
	 VB.Free++;							\
	 if (VB.Free==VB_SIZE)  VB.Free = 1;				\
      }                                                 		\
      /* else both verts are inside ==> do nothing */   		\
   }


#define X(I)	VB.Clip[I][0]
#define Y(I)	VB.Clip[I][1]
#define Z(I)	VB.Clip[I][2]
#define W(I)	VB.Clip[I][3]

/*
 * Begin clipping
 */

   /*** Clip against +X side ***/
#define OUTSIDE(K)      (X(K) > W(K))
#define COMPUTE_INTERSECTION( new, in, out )		\
	dx = X(out) - X(in);				\
	dw = W(out) - W(in);				\
	t = (X(in) - W(in)) / (dw-dx);			\
	X(new) = X(in) + t * dx;			\
	Y(new) = Y(in) + t * (Y(out) - Y(in));		\
	Z(new) = Z(in) + t * (Z(out) - Z(in));		\
	W(new) = W(in) + t * dw;

   GENERAL_CLIP

#undef OUTSIDE
#undef COMPUTE_INTERSECTION


   /*** Clip against -X side ***/
#define OUTSIDE(K)      (X(K) < -W(K))
#define COMPUTE_INTERSECTION( new, in, out )		\
	dx = X(out) - X(in);				\
	dw = W(out) - W(in);				\
        t = -(X(in) + W(in)) / (dw+dx);			\
	X(new) = X(in) + t * dx;			\
	Y(new) = Y(in) + t * (Y(out) - Y(in));		\
	Z(new) = Z(in) + t * (Z(out) - Z(in));		\
	W(new) = W(in) + t * dw;

   GENERAL_CLIP

#undef OUTSIDE
#undef COMPUTE_INTERSECTION


   /*** Clip against +Y side ***/
#define OUTSIDE(K)      (Y(K) > W(K))
#define COMPUTE_INTERSECTION( new, in, out )		\
	dy = Y(out) - Y(in);				\
	dw = W(out) - W(in);				\
        t = (Y(in) - W(in)) / (dw-dy);			\
	X(new) = X(in) + t * (X(out) - X(in));		\
	Y(new) = Y(in) + t * dy;			\
	Z(new) = Z(in) + t * (Z(out) - Z(in));		\
	W(new) = W(in) + t * dw;

   GENERAL_CLIP

#undef OUTSIDE
#undef COMPUTE_INTERSECTION


   /*** Clip against -Y side ***/
#define OUTSIDE(K)      (Y(K) < -W(K))
#define COMPUTE_INTERSECTION( new, in, out )		\
        dy = Y(out) - Y(in);				\
        dw = W(out) - W(in);				\
        t = -(Y(in) + W(in)) / (dw+dy);			\
        X(new) = X(in) + t * (X(out) - X(in));		\
	Y(new) = Y(in) + t * dy;			\
	Z(new) = Z(in) + t * (Z(out) - Z(in));		\
	W(new) = W(in) + t * dw;

   GENERAL_CLIP

#undef OUTSIDE
#undef COMPUTE_INTERSECTION


   /*** Clip against +Z side ***/
#define OUTSIDE(K)      (Z(K) > W(K))
#define COMPUTE_INTERSECTION( new, in, out )		\
        dz = Z(out) - Z(in);				\
        dw = W(out) - W(in);				\
        t = (Z(in) - W(in)) / (dw-dz);			\
        X(new) = X(in) + t * (X(out) - X(in));		\
        Y(new) = Y(in) + t * (Y(out) - Y(in));		\
	Z(new) = Z(in) + t * dz;			\
	W(new) = W(in) + t * dw;

   GENERAL_CLIP

#undef OUTSIDE
#undef COMPUTE_INTERSECTION


   /*** Clip against -Z side ***/
#define OUTSIDE(K)      (Z(K) < -W(K))
#define COMPUTE_INTERSECTION( new, in, out )		\
        dz = Z(out) - Z(in);				\
        dw = W(out) - W(in);				\
        t = -(Z(in) + W(in)) / (dw+dz);			\
        X(new) = X(in) + t * (X(out) - X(in));		\
        Y(new) = Y(in) + t * (Y(out) - Y(in));		\
	Z(new) = Z(in) + t * dz;			\
	W(new) = W(in) + t * dw;

   GENERAL_CLIP

#undef OUTSIDE
#undef COMPUTE_INTERSECTION

#undef GENERAL_CLIP

   *i = ii;
   *j = jj;
   return 1;
}




/*
 * Clip a polygon against the view volume defined by -w<=x,y,z<=w.
 * Input:  n - number of vertices in input polygon.
 *         vlist - list of indexes into VB.V* of polygon to clip.
 * Output:  vlist - modified list of vertex indexes
 * Return:  number of vertices in resulting polygon
 */
GLuint gl_viewclip_polygon( GLuint n, GLuint vlist[] )

{
   GLuint previ, prevj;
   GLuint curri, currj;
   GLuint vlist2[VB_MAX];
   GLuint n2;
   GLfloat dx, dy, dz, dw, t;
   GLuint incount, i;

   Space = CLIP_SPACE;

   /* Trivial inside clipping test. */
   incount = 0;
   for (i=0;i<n;i++) {
      GLuint j = vlist[i];
      GLfloat vi3 = VB.Clip[j][3];
      if (VB.Clip[j][0] <= vi3 && VB.Clip[j][0] >= -vi3 &&
	  VB.Clip[j][1] <= vi3 && VB.Clip[j][1] >= -vi3 &&
	  VB.Clip[j][2] <= vi3 && VB.Clip[j][2] >= -vi3) {
	 incount++;
      }
   }
   if (incount==n) {
      return n;
   }


/*
 * We use 6 instances of this code to implement clipping against the
 * 6 sides of the view volume.  Prior to each we define the macros:
 *    INLIST = array which lists input vertices
 *    OUTLIST = array which lists output vertices
 *    INCOUNT = variable which is the number of vertices in INLIST[]
 *    OUTCOUNT = variable which is the number of vertices in OUTLIST[]
 *    INSIDE(J) = test if vertex[J] is inside the view volume
 *    COMPUTE_INTERSECTION(inv,outv,newv) = compute intersection of line
 *              from inv[] to outv[] with the clipping plane and store
 *              the result in newv[]
 */

#define GENERAL_CLIP                                                    \
   if (INCOUNT<3)  return 0;						\
   previ = INCOUNT-1;		/* let previous = last vertex */	\
   prevj = INLIST[previ];						\
   OUTCOUNT = 0;                                                        \
   for (curri=0;curri<INCOUNT;curri++) {				\
      currj = INLIST[curri];						\
      if (INSIDE(currj)) {						\
         if (INSIDE(prevj)) {						\
            /* both verts are inside ==> copy current to outlist */     \
	    OUTLIST[OUTCOUNT] = currj;					\
	    OUTCOUNT++;							\
         }                                                              \
         else {                                                         \
            /* current is inside and previous is outside ==> clip */	\
	    COMPUTE_INTERSECTION( VB.Clip[currj], VB.Clip[prevj],	\
					 VB.Clip[VB.Free] )		\
	    /* interpolate aux info using the value of t */		\
	    if (CC.ClipMask)  interpolate_aux( VB.Free, t, currj, prevj ); \
	    VB.Edgeflag[VB.Free] = VB.Edgeflag[prevj];		\
	    OUTLIST[OUTCOUNT] = VB.Free;				\
	    VB.Free++;							\
	    if (VB.Free==VB_SIZE)   VB.Free = 1;			\
	    OUTCOUNT++;							\
	    /* Output current */					\
	    OUTLIST[OUTCOUNT] = currj;					\
	    OUTCOUNT++;							\
         }                                                              \
      }                                                                 \
      else {                                                            \
         if (INSIDE(prevj)) {                                       	\
            /* current is outside and previous is inside ==> clip */	\
	    COMPUTE_INTERSECTION( VB.Clip[prevj], VB.Clip[currj],	\
					VB.Clip[VB.Free] )		\
	    /* interpolate aux info using the value of t */		\
	    if (CC.ClipMask)  interpolate_aux( VB.Free, t, prevj, currj ); \
	    VB.Edgeflag[VB.Free] = VB.Edgeflag[prevj];		\
	    OUTLIST[OUTCOUNT] = VB.Free;				\
	    VB.Free++;							\
	    if (VB.Free==VB_SIZE)   VB.Free = 1;			\
	    OUTCOUNT++;							\
         }                                                              \
         /* else both verts are outside ==> do nothing */               \
      }                                                                 \
      /* let previous = current */					\
      previ = curri;							\
      prevj = currj;							\
   }


/*
 * Clip against +X
 */
#define INCOUNT n
#define OUTCOUNT n2
#define INLIST vlist
#define OUTLIST vlist2
#define INSIDE(J)      ( VB.Clip[J][0] <= VB.Clip[J][3] )

#define COMPUTE_INTERSECTION( inv, outv, newv )		\
        dx = outv[0] - inv[0];				\
        dw = outv[3] - inv[3];				\
        t = (inv[0]-inv[3]) / (dw-dx);			\
	newv[0] = inv[0] + t * dx;			\
	newv[1] = inv[1] + t * (outv[1]-inv[1]);	\
	newv[2] = inv[2] + t * (outv[2]-inv[2]); 	\
	newv[3] = inv[3] + t * dw;

   GENERAL_CLIP

#undef INCOUNT
#undef OUTCOUNT
#undef INLIST
#undef OUTLIST
#undef INSIDE
#undef COMPUTE_INTERSECTION


/*
 * Clip against -X
 */
#define INCOUNT n2
#define OUTCOUNT n
#define INLIST vlist2
#define OUTLIST vlist
#define INSIDE(J)       (VB.Clip[J][0] >= -VB.Clip[J][3])

#define COMPUTE_INTERSECTION( inv, outv, newv )		\
        dx = outv[0]-inv[0];                      	\
        dw = outv[3]-inv[3];                      	\
        t = -(inv[0]+inv[3]) / (dw+dx);           	\
        newv[0] = inv[0] + t * dx;                	\
        newv[1] = inv[1] + t * (outv[1]-inv[1]);	\
        newv[2] = inv[2] + t * (outv[2]-inv[2]);	\
        newv[3] = inv[3] + t * dw;

   GENERAL_CLIP

#undef INCOUNT
#undef OUTCOUNT
#undef INLIST
#undef OUTLIST
#undef INSIDE
#undef COMPUTE_INTERSECTION


/*
 * Clip against +Y
 */
#define INCOUNT n
#define OUTCOUNT n2
#define INLIST vlist
#define OUTLIST vlist2
#define INSIDE(J)       (VB.Clip[J][1] <= VB.Clip[J][3])
#define COMPUTE_INTERSECTION( inv, outv, newv )		\
        dy = outv[1]-inv[1];                      	\
        dw = outv[3]-inv[3];                      	\
        t = (inv[1]-inv[3]) / (dw-dy);            	\
        newv[0] = inv[0] + t * (outv[0]-inv[0]);	\
        newv[1] = inv[1] + t * dy;			\
        newv[2] = inv[2] + t * (outv[2]-inv[2]);	\
        newv[3] = inv[3] + t * dw;

   GENERAL_CLIP

#undef INCOUNT
#undef OUTCOUNT
#undef INLIST
#undef OUTLIST
#undef INSIDE
#undef COMPUTE_INTERSECTION


/*
 * Clip against -Y
 */
#define INCOUNT n2
#define OUTCOUNT n
#define INLIST vlist2
#define OUTLIST vlist
#define INSIDE(J)       (VB.Clip[J][1] >= -VB.Clip[J][3])
#define COMPUTE_INTERSECTION( inv, outv, newv )		\
        dy = outv[1]-inv[1];                      	\
        dw = outv[3]-inv[3];                      	\
        t = -(inv[1]+inv[3]) / (dw+dy);           	\
        newv[0] = inv[0] + t * (outv[0]-inv[0]);	\
        newv[1] = inv[1] + t * dy;                	\
        newv[2] = inv[2] + t * (outv[2]-inv[2]);	\
        newv[3] = inv[3] + t * dw;

   GENERAL_CLIP

#undef INCOUNT
#undef OUTCOUNT
#undef INLIST
#undef OUTLIST
#undef INSIDE
#undef COMPUTE_INTERSECTION



/*
 * Clip against +Z
 */
#define INCOUNT n
#define OUTCOUNT n2
#define INLIST vlist
#define OUTLIST vlist2
#define INSIDE(J)       (VB.Clip[J][2] <= VB.Clip[J][3])
#define COMPUTE_INTERSECTION( inv, outv, newv )		\
        dz = outv[2]-inv[2];                      	\
        dw = outv[3]-inv[3];                      	\
        t = (inv[2]-inv[3]) / (dw-dz);            	\
        newv[0] = inv[0] + t * (outv[0]-inv[0]);	\
        newv[1] = inv[1] + t * (outv[1]-inv[1]);	\
        newv[2] = inv[2] + t * dz;                	\
        newv[3] = inv[3] + t * dw;

   GENERAL_CLIP

#undef INCOUNT
#undef OUTCOUNT
#undef INLIST
#undef OUTLIST
#undef INSIDE
#undef COMPUTE_INTERSECTION


/*
 * Clip against -Z
 */
#define INCOUNT n2
#define OUTCOUNT n
#define INLIST vlist2
#define OUTLIST vlist
#define INSIDE(J)       (VB.Clip[J][2] >= -VB.Clip[J][3])
#define COMPUTE_INTERSECTION( inv, outv, newv )		\
        dz = outv[2]-inv[2];                      	\
        dw = outv[3]-inv[3];                      	\
        t = -(inv[2]+inv[3]) / (dw+dz);           	\
        newv[0] = inv[0] + t * (outv[0]-inv[0]);	\
        newv[1] = inv[1] + t * (outv[1]-inv[1]);	\
        newv[2] = inv[2] + t * dz;                	\
        newv[3] = inv[3] + t * dw;

   GENERAL_CLIP

#undef INCOUNT
#undef INLIST
#undef OUTLIST
#undef INSIDE
#undef COMPUTE_INTERSECTION

   /* 'OUTCOUNT' clipped vertices are now back in v[] */
   return OUTCOUNT;

#undef GENERAL_CLIP
#undef OUTCOUNT
}




/**********************************************************************/
/*         Clipping against user-defined clipping planes.             */
/**********************************************************************/



/*
 * If the dot product of the eye coordinates of a vertex with the
 * stored plane equation components is positive or zero, the vertex
 * is in with respect to that clipping plane, otherwise it is out.
 */



/*
 * Clip a point against the user clipping planes.
 * Input:  v - vertex-vector describing the point to clip.
 * Return:  0 = point was clipped
 *          1 = point not clipped
 */
GLuint gl_userclip_point( const GLfloat v[] )
{
   GLuint p;

   for (p=0;p<MAX_CLIP_PLANES;p++) {
      if (CC.Transform.ClipEnabled[p]) {
	 GLfloat dot = v[0] * CC.Transform.ClipEquation[p][0]
		     + v[1] * CC.Transform.ClipEquation[p][1]
		     + v[2] * CC.Transform.ClipEquation[p][2]
		     + v[3] * CC.Transform.ClipEquation[p][3];
         if (dot < 0.0F) {
            return 0;
         }
      }
   }

   return 1;
}


#define MAGIC_NUMBER -0.8e-03F


/* Test if VB.Eye[J] is inside the clipping plane defined by A,B,C,D */
#define INSIDE( J, A, B, C, D )   				\
   ( (VB.Eye[J][0] * A + VB.Eye[J][1] * B			\
    + VB.Eye[J][2] * C + VB.Eye[J][3] * D) >= MAGIC_NUMBER )


/* Test if VB.Eye[J] is outside the clipping plane defined by A,B,C,D */
#define OUTSIDE( J, A, B, C, D )   				\
   ( (VB.Eye[J][0] * A + VB.Eye[J][1] * B			\
    + VB.Eye[J][2] * C + VB.Eye[J][3] * D) < MAGIC_NUMBER )


/*
 * Clip a line against the user clipping planes.
 * Input:  i, j - indexes into VB.V*[] of endpoints
 * Output:  i, j - indexes into VB.V*[] of (possibly clipped) endpoints
 * Return:  0 = line completely clipped
 *          1 = line is visible
 */
GLuint gl_userclip_line( GLuint *i, GLuint *j )
{
   GLuint p, ii, jj;

   Space = EYE_SPACE;

   ii = *i;
   jj = *j;

   for (p=0;p<MAX_CLIP_PLANES;p++) {
      if (CC.Transform.ClipEnabled[p]) {
	 register GLfloat a, b, c, d;
	 a = CC.Transform.ClipEquation[p][0];
	 b = CC.Transform.ClipEquation[p][1];
	 c = CC.Transform.ClipEquation[p][2];
	 d = CC.Transform.ClipEquation[p][3];

         if (OUTSIDE( ii, a,b,c,d  )) {
            if (OUTSIDE( jj, a,b,c,d )) {
               /* ii and jj outside ==> quit */
               return 0;
            }
            else {
               /* ii is outside, jj is inside ==> clip */
               GLfloat dx, dy, dz, dw, t, denom;
               dx = VB.Eye[ii][0] - VB.Eye[jj][0];
               dy = VB.Eye[ii][1] - VB.Eye[jj][1];
               dz = VB.Eye[ii][2] - VB.Eye[jj][2];
               dw = VB.Eye[ii][3] - VB.Eye[jj][3];
	       denom = dx*a + dy*b + dz*c + dw*d;
	       if (denom==0.0) {
		  t = 0.0;
	       }
	       else {
		  t = -(VB.Eye[jj][0]*a+VB.Eye[jj][1]*b
		       +VB.Eye[jj][2]*c+VB.Eye[jj][3]*d) / denom;
                  if (t>1.0F)  t = 1.0F;
	       }
	       VB.Eye[VB.Free][0] = VB.Eye[jj][0] + t * dx;
	       VB.Eye[VB.Free][1] = VB.Eye[jj][1] + t * dy;
	       VB.Eye[VB.Free][2] = VB.Eye[jj][2] + t * dz;
	       VB.Eye[VB.Free][3] = VB.Eye[jj][3] + t * dw;

	       /* Interpolate colors, indexes, and/or texture coords */
	       if (CC.ClipMask)  interpolate_aux( VB.Free, t, jj, ii );

	       ii = VB.Free;
	       VB.Free++;
	       if (VB.Free==VB_SIZE)   VB.Free = 1;
            }
         }
         else {
            if (OUTSIDE( jj, a,b,c,d )) {
               /* ii is inside, jj is outside ==> clip */
               GLfloat dx, dy, dz, dw, t, denom;
               dx = VB.Eye[jj][0] - VB.Eye[ii][0];
               dy = VB.Eye[jj][1] - VB.Eye[ii][1];
               dz = VB.Eye[jj][2] - VB.Eye[ii][2];
               dw = VB.Eye[jj][3] - VB.Eye[ii][3];
	       denom = dx*a + dy*b + dz*c + dw*d;
	       if (denom==0.0) {
		  t = 0.0;
	       }
	       else {
		  t = -(VB.Eye[ii][0]*a+VB.Eye[ii][1]*b
		       +VB.Eye[ii][2]*c+VB.Eye[ii][3]*d) / denom;
                  if (t>1.0F)  t = 1.0F;
	       }
	       VB.Eye[VB.Free][0] = VB.Eye[ii][0] + t * dx;
	       VB.Eye[VB.Free][1] = VB.Eye[ii][1] + t * dy;
	       VB.Eye[VB.Free][2] = VB.Eye[ii][2] + t * dz;
	       VB.Eye[VB.Free][3] = VB.Eye[ii][3] + t * dw;

	       /* Interpolate colors, indexes, and/or texture coords */
	       if (CC.ClipMask)  interpolate_aux( VB.Free, t, ii, jj );

	       jj = VB.Free;
	       VB.Free++;
	       if (VB.Free==VB_SIZE)   VB.Free = 1;
            }
            else {
               /* ii and jj inside ==> do nothing */
            }
         }
      }
   }

   *i = ii;
   *j = jj;
   return 1;
}




/*
 * Clip a polygon against the user clipping planes defined in eye coordinates.
 * Input:  n - number of vertices.
 *         vlist - list of vertices in input polygon.
 * Output:  vlist - list of vertices in output polygon.
 * Return:  number of vertices after clipping.
 */
GLuint gl_userclip_polygon( GLuint n, GLuint vlist[] )
{
   GLuint vlist2[VB_MAX];
   GLuint *inlist, *outlist;
   GLuint incount, outcount;
   GLuint curri, currj;
   GLuint previ, prevj;
   GLuint p;

   Space = EYE_SPACE;

   /* initialize input vertex list */
   incount = n;
   inlist = vlist;
   outlist = vlist2;

   for (p=0;p<MAX_CLIP_PLANES;p++) {
      if (CC.Transform.ClipEnabled[p]) {
	 register float a = CC.Transform.ClipEquation[p][0];
	 register float b = CC.Transform.ClipEquation[p][1];
	 register float c = CC.Transform.ClipEquation[p][2];
	 register float d = CC.Transform.ClipEquation[p][3];

	 if (incount<3)  return 0;

	 /* initialize prev to be last in the input list */
	 previ = incount - 1;
	 prevj = inlist[previ];

         outcount = 0;

         for (curri=0;curri<incount;curri++) {
	    currj = inlist[curri];

            if (INSIDE(currj, a,b,c,d)) {
               if (INSIDE(prevj, a,b,c,d)) {
                  /* both verts are inside ==> copy current to outlist */
		  outlist[outcount++] = currj;
               }
               else {
                  /* current is inside and previous is outside ==> clip */
                  GLfloat dx, dy, dz, dw, t, denom;
		  /* compute t */
		  dx = VB.Eye[prevj][0] - VB.Eye[currj][0];
		  dy = VB.Eye[prevj][1] - VB.Eye[currj][1];
		  dz = VB.Eye[prevj][2] - VB.Eye[currj][2];
		  dw = VB.Eye[prevj][3] - VB.Eye[currj][3];
		  denom = dx*a + dy*b + dz*c + dw*d;
		  if (denom==0.0) {
		     t = 0.0;
		  }
		  else {
		     t = -(VB.Eye[currj][0]*a+VB.Eye[currj][1]*b
		       +VB.Eye[currj][2]*c+VB.Eye[currj][3]*d) / denom;
                     if (t>1.0F) {
                        /*printf("t1=%g\n", t);*/
                        t = 1.0F;
                     }
		  }
		  /* interpolate new vertex position */
		  VB.Eye[VB.Free][0] = VB.Eye[currj][0] + t*dx;
		  VB.Eye[VB.Free][1] = VB.Eye[currj][1] + t*dy;
		  VB.Eye[VB.Free][2] = VB.Eye[currj][2] + t*dz;
		  VB.Eye[VB.Free][3] = VB.Eye[currj][3] + t*dw;

		  /* interpolate color, index, and/or texture coord */
		  if (CC.ClipMask) interpolate_aux( VB.Free, t, currj, prevj);
		  VB.Edgeflag[VB.Free] = VB.Edgeflag[prevj];

		  /* output new vertex */
		  outlist[outcount++] = VB.Free;
		  VB.Free++;
		  if (VB.Free==VB_SIZE)   VB.Free = 1;
		  /* output current vertex */
		  outlist[outcount++] = currj;
               }
            }
            else {
               if (INSIDE(prevj, a,b,c,d)) {
                  /* current is outside and previous is inside ==> clip */
                  GLfloat dx, dy, dz, dw, t, denom;
		  /* compute t */
                  dx = VB.Eye[currj][0]-VB.Eye[prevj][0];
                  dy = VB.Eye[currj][1]-VB.Eye[prevj][1];
                  dz = VB.Eye[currj][2]-VB.Eye[prevj][2];
                  dw = VB.Eye[currj][3]-VB.Eye[prevj][3];
		  denom = dx*a + dy*b + dz*c + dw*d;
		  if (denom==0.0) {
		     t = 0.0;
		  }
		  else {
		     t = -(VB.Eye[prevj][0]*a+VB.Eye[prevj][1]*b
		       +VB.Eye[prevj][2]*c+VB.Eye[prevj][3]*d) / denom;
                     if (t>1.0F) {
                        /*printf("t2=%g\n", t);*/
                        t = 1.0F;
                     }
		  }
		  /* interpolate new vertex position */
		  VB.Eye[VB.Free][0] = VB.Eye[prevj][0] + t*dx;
		  VB.Eye[VB.Free][1] = VB.Eye[prevj][1] + t*dy;
		  VB.Eye[VB.Free][2] = VB.Eye[prevj][2] + t*dz;
		  VB.Eye[VB.Free][3] = VB.Eye[prevj][3] + t*dw;

		  /* interpolate color, index, and/or texture coord */
		  if (CC.ClipMask) interpolate_aux( VB.Free, t, prevj, currj);
		  VB.Edgeflag[VB.Free] = VB.Edgeflag[prevj];

		  /* output new vertex */
		  outlist[outcount++] = VB.Free;
		  VB.Free++;
		  if (VB.Free==VB_SIZE)   VB.Free = 1;
	       }
               /* else  both verts are outside ==> do nothing */
            }

	    previ = curri;
	    prevj = currj;

         }  /* for i */

         /* swap inv and outv pointers */
         {
            GLuint *tmp;
            tmp = inlist;
            inlist = outlist;
            outlist = tmp;
            incount = outcount;
         }

      } /* if */
   } /* for p */

   /* outlist points to the list of vertices resulting from the last */
   /* clipping.  If outlist == vlist2 then we have to copy the vertices */
   /* back to vlist */
   if (outlist!=vlist2) {
      MEMCPY( vlist, vlist2, outcount * sizeof(GLuint) );
   }

   return outcount;
}

