/**************************************************************************
 *									  *
 * 		 Copyright (C) 1991, Silicon Graphics, Inc.		  *
 *									  *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
 *  are protected by Federal copyright law.  They  may  not be disclosed  *
 *  to  third  parties  or copied or duplicated in any form, in whole or  *
 *  in part, without the prior written consent of Silicon Graphics, Inc.  *
 *									  *
 **************************************************************************/

/*
 * glsurfeval.c++ - surface evaluator
 *
 * $Revision: 1.5 $
 */

/* Polynomial Evaluator Interface */

#include <glos.h>
#include <GL/gl.h>
#include "glimport.h"
#include "glrender.h"
#include "glsurfev.h"
#include "nurbscon.h"

/*#define USE_INTERNAL_EVAL*/ //use internal evaluator

/*whether do evaluation or not*/
/*#define NO_EVALUATION*/

 
/*for statistics*/
/*#define STATISTICS*/
#ifdef STATISTICS
static int STAT_num_of_triangles=0;
static int STAT_num_of_eval_vertices=0;
static int STAT_num_of_quad_strips=0;
#endif 


OpenGLSurfaceEvaluator::OpenGLSurfaceEvaluator() 
{ 
    int i;

    for (i=0; i<VERTEX_CACHE_SIZE; i++) {
	vertexCache[i] = new StoredVertex;
    }
    tmeshing = 0;
    which = 0;
    vcount = 0;


}

OpenGLSurfaceEvaluator::~OpenGLSurfaceEvaluator() 
{ 
   for (int ii= 0; ii< VERTEX_CACHE_SIZE; ii++) {
      delete vertexCache[ii];
      vertexCache[ii]= 0;
   }
}

/*---------------------------------------------------------------------------
 * disable - turn off a map
 *---------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::disable(long type)
{
    glDisable((GLenum) type);
}

/*---------------------------------------------------------------------------
 * enable - turn on a map
 *---------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::enable(long type)
{
    glEnable((GLenum) type);
}

/*-------------------------------------------------------------------------
 * mapgrid2f - define a lattice of points with origin and offset
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::mapgrid2f(long nu, REAL u0, REAL u1, long nv, REAL v0, REAL v1)
{
#ifdef USE_INTERNAL_EVAL
  inMapGrid2f((int) nu, (REAL) u0, (REAL) u1, (int) nv, 
	      (REAL) v0, (REAL) v1);
#else

    glMapGrid2d((GLint) nu, (GLdouble) u0, (GLdouble) u1, (GLint) nv, 
	    (GLdouble) v0, (GLdouble) v1);
#endif
}

void
OpenGLSurfaceEvaluator::polymode(long style)
{
    switch(style) {
    default:
    case N_MESHFILL:

	glPolygonMode((GLenum) GL_FRONT_AND_BACK, (GLenum) GL_FILL);
	break;
    case N_MESHLINE:
	glPolygonMode((GLenum) GL_FRONT_AND_BACK, (GLenum) GL_LINE);
	break;
    case N_MESHPOINT:
	glPolygonMode((GLenum) GL_FRONT_AND_BACK, (GLenum) GL_POINT);
	break;
    }
}

void
OpenGLSurfaceEvaluator::bgnline(void)
{
    glBegin((GLenum) GL_LINE_STRIP);
}

void
OpenGLSurfaceEvaluator::endline(void)
{
    glEnd();
}

void
OpenGLSurfaceEvaluator::range2f(long type, REAL *from, REAL *to)
{
}

void
OpenGLSurfaceEvaluator::domain2f(REAL ulo, REAL uhi, REAL vlo, REAL vhi)
{
}

void
OpenGLSurfaceEvaluator::bgnclosedline(void)
{
    glBegin((GLenum) GL_LINE_LOOP);
}

void
OpenGLSurfaceEvaluator::endclosedline(void)
{
    glEnd();
}





void
OpenGLSurfaceEvaluator::bgntmesh(void)
{

    tmeshing = 1;
    which = 0;
    vcount = 0;

    glBegin((GLenum) GL_TRIANGLES);

}

void
OpenGLSurfaceEvaluator::swaptmesh(void)
{
    which = 1 - which;

}

void
OpenGLSurfaceEvaluator::endtmesh(void)
{
    tmeshing = 0;

    glEnd();
}

void
OpenGLSurfaceEvaluator::bgntfan(void)
{
 glBegin((GLenum) GL_TRIANGLE_FAN);
}
void
OpenGLSurfaceEvaluator::endtfan(void)
{
 glEnd();
}

void
OpenGLSurfaceEvaluator::evalUStrip(int n_upper, REAL v_upper, REAL* upper_val, int n_lower, REAL v_lower, REAL* lower_val)
{
#ifdef USE_INTERNAL_EVAL
  inEvalUStrip(n_upper, v_upper, upper_val,
	n_lower, v_lower, lower_val);
#else
  int i,j,k,l;
  REAL leftMostV[2];

  /*
   *the algorithm works by scanning from left to right.
   *leftMostV: the left most of the remaining verteces (on both upper and lower).
   *           it could an element of upperVerts or lowerVerts.
   *i: upperVerts[i] is the first vertex to the right of leftMostV on upper line   
   *j: lowerVerts[j] is the first vertex to the right of leftMostV on lower line   
   */
  
  /*initialize i,j,and leftMostV
   */
  if(upper_val[0] <= lower_val[0])
    {
      i=1;
      j=0;

      leftMostV[0] = upper_val[0];
      leftMostV[1] = v_upper;
    }
  else
    {
      i=0;
      j=1;

      leftMostV[0] = lower_val[0];
      leftMostV[1] = v_lower;

    }
  
  /*the main loop.
   *the invariance is that: 
   *at the beginning of each loop, the meaning of i,j,and leftMostV are 
   *maintained
   */
  while(1)
    {
      if(i >= n_upper) /*case1: no more in upper*/
        {
          if(j<n_lower-1) /*at least two vertices in lower*/
            {
              bgntfan();
	      coord2f(leftMostV[0], leftMostV[1]);
//	      glNormal3fv(leftMostNormal);
//              glVertex3fv(leftMostXYZ);

              while(j<n_lower){
		coord2f(lower_val[j], v_lower);
//		glNormal3fv(lowerNormal[j]);
//		glVertex3fv(lowerXYZ[j]);
		j++;

              }
              endtfan();
            }
          break; /*exit the main loop*/
        }
      else if(j>= n_lower) /*case2: no more in lower*/
        {
          if(i<n_upper-1) /*at least two vertices in upper*/
            {
              bgntfan();
	      coord2f(leftMostV[0], leftMostV[1]);
//	      glNormal3fv(leftMostNormal);
//	      glVertex3fv(leftMostXYZ);
	      
              for(k=n_upper-1; k>=i; k--) /*reverse order for two-side lighting*/
		{
		  coord2f(upper_val[k], v_upper);
//		  glNormal3fv(upperNormal[k]);
//		  glVertex3fv(upperXYZ[k]);
		}

              endtfan();
            }
          break; /*exit the main loop*/
        }
      else /* case3: neither is empty, plus the leftMostV, there is at least one triangle to output*/
        {
          if(upper_val[i] <= lower_val[j])
            {
	      bgntfan();
	      coord2f(lower_val[j], v_lower);
//	      glNormal3fv(lowerNormal[j]);
//	      glVertex3fv(lowerXYZ[j]);

              /*find the last k>=i such that 
               *upperverts[k][0] <= lowerverts[j][0]
               */
              k=i;

              while(k<n_upper)
                {
                  if(upper_val[k] > lower_val[j])
                    break;
                  k++;

                }
              k--;


              for(l=k; l>=i; l--)/*the reverse is for two-side lighting*/
                {
		  coord2f(upper_val[l], v_upper);
//		  glNormal3fv(upperNormal[l]);
//		  glVertex3fv(upperXYZ[l]);

                }
	      coord2f(leftMostV[0], leftMostV[1]);
//	      glNormal3fv(leftMostNormal);
//	      glVertex3fv(leftMostXYZ);

              endtfan();

              /*update i and leftMostV for next loop
               */
              i = k+1;

	      leftMostV[0] = upper_val[k];
	      leftMostV[1] = v_upper;
//	      leftMostNormal = upperNormal[k];
//	      leftMostXYZ = upperXYZ[k];
            }
          else /*upperVerts[i][0] > lowerVerts[j][0]*/
            {
	      bgntfan();
	      coord2f(upper_val[i], v_upper);
//	      glNormal3fv(upperNormal[i]);
//	      glVertex3fv(upperXYZ[i]);
	
	      coord2f(leftMostV[0], leftMostV[1]);
//              glNormal3fv(leftMostNormal);
//	      glVertex3fv(leftMostXYZ);
	      

              /*find the last k>=j such that
               *lowerverts[k][0] < upperverts[i][0]
               */
              k=j;
              while(k< n_lower)
                {
                  if(lower_val[k] >= upper_val[i])
                    break;
		  coord2f(lower_val[k], v_lower);
//		  glNormal3fv(lowerNormal[k]);
//		  glVertex3fv(lowerXYZ[k]);

                  k++;
                }
              endtfan();

              /*update j and leftMostV for next loop
               */
              j=k;
	      leftMostV[0] = lower_val[j-1];
	      leftMostV[1] = v_lower;

//	      leftMostNormal = lowerNormal[j-1];
//	      leftMostXYZ = lowerXYZ[j-1];
            }     
        }
    }
  //clean up 
//  free(upperXYZ);
//  free(lowerXYZ);
//  free(upperNormal);
//  free(lowerNormal);
#endif

}
  

void
OpenGLSurfaceEvaluator::evalVStrip(int n_left, REAL u_left, REAL* left_val, int n_right, REAL u_right, REAL* right_val)
{
#ifdef USE_INTERNAL_EVAL
	inEvalVStrip(n_left, u_left, left_val,
	n_right, u_right, right_val);
#else
  int i,j,k,l;
  REAL botMostV[2];
  /*
   *the algorithm works by scanning from bot to top.
   *botMostV: the bot most of the remaining verteces (on both left and right).
   *           it could an element of leftVerts or rightVerts.
   *i: leftVerts[i] is the first vertex to the top of botMostV on left line   
   *j: rightVerts[j] is the first vertex to the top of botMostV on rightline
   */
  
  /*initialize i,j,and botMostV
   */
  if(left_val[0] <= right_val[0])
    {
      i=1;
      j=0;

      botMostV[0] = u_left;
      botMostV[1] = left_val[0];
    }
  else
    {
      i=0;
      j=1;

      botMostV[0] = u_right;
      botMostV[1] = right_val[0];
    }

  /*the main loop.
   *the invariance is that: 
   *at the beginning of each loop, the meaning of i,j,and botMostV are 
   *maintained
   */
  while(1)
    {
      if(i >= n_left) /*case1: no more in left*/
        {
          if(j<n_right-1) /*at least two vertices in right*/
            {
              bgntfan();
	      coord2f(botMostV[0], botMostV[1]);
              while(j<n_right){
		coord2f(u_right, right_val[j]);
//		glNormal3fv(rightNormal[j]);
//		glVertex3fv(rightXYZ[j]);
		j++;

              }
              endtfan();
            }
          break; /*exit the main loop*/
        }
      else if(j>= n_right) /*case2: no more in right*/
        {
          if(i<n_left-1) /*at least two vertices in left*/
            {
              bgntfan();
              coord2f(botMostV[0], botMostV[1]);
//	      glNormal3fv(botMostNormal);
//	      glVertex3fv(botMostXYZ);
	      
              for(k=n_left-1; k>=i; k--) /*reverse order for two-side lighting*/
		{
		  coord2f(u_left, left_val[k]);
//		  glNormal3fv(leftNormal[k]);
//		  glVertex3fv(leftXYZ[k]);
		}

              endtfan();
            }
          break; /*exit the main loop*/
        }
      else /* case3: neither is empty, plus the botMostV, there is at least one triangle to output*/
        {
          if(left_val[i] <= right_val[j])
            {
	      bgntfan();
	      coord2f(u_right, right_val[j]);
//	      glNormal3fv(rightNormal[j]);
//	      glVertex3fv(rightXYZ[j]);

              /*find the last k>=i such that 
               *leftverts[k][0] <= rightverts[j][0]
               */
              k=i;

              while(k<n_left)
                {
                  if(left_val[k] > right_val[j])
                    break;
                  k++;

                }
              k--;


              for(l=k; l>=i; l--)/*the reverse is for two-side lighting*/
                {
		  coord2f(u_left, left_val[l]);
//		  glNormal3fv(leftNormal[l]);
//		  glVertex3fv(leftXYZ[l]);

                }
	      coord2f(botMostV[0], botMostV[1]);
//	      glNormal3fv(botMostNormal);
//	      glVertex3fv(botMostXYZ);

              endtfan();

              /*update i and botMostV for next loop
               */
              i = k+1;

	      botMostV[0] = u_left;
	      botMostV[1] = left_val[k];
//	      botMostNormal = leftNormal[k];
//	      botMostXYZ = leftXYZ[k];
            }
          else /*left_val[i] > right_val[j])*/
            {
	      bgntfan();
	      coord2f(u_left, left_val[i]);
//	      glNormal3fv(leftNormal[i]);
//	      glVertex3fv(leftXYZ[i]);
	      
	      coord2f(botMostV[0], botMostV[1]);
//            glNormal3fv(botMostNormal);
//	      glVertex3fv(botMostXYZ);
	      

              /*find the last k>=j such that
               *rightverts[k][0] < leftverts[i][0]
               */
              k=j;
              while(k< n_right)
                {
                  if(right_val[k] >= left_val[i])
                    break;
		  coord2f(u_right, right_val[k]);
//		  glNormal3fv(rightNormal[k]);
//		  glVertex3fv(rightXYZ[k]);

                  k++;
                }
              endtfan();

              /*update j and botMostV for next loop
               */
              j=k;
	      botMostV[0] = u_right;
	      botMostV[1] = right_val[j-1];

//	      botMostNormal = rightNormal[j-1];
//	      botMostXYZ = rightXYZ[j-1];
            }     
        }
    }
  //clean up 
//  free(leftXYZ);
//  free(leftNormal);
//  free(rightXYZ);
//  free(rightNormal);
#endif
}
  

void
OpenGLSurfaceEvaluator::bgnqstrip(void)
{
    glBegin((GLenum) GL_QUAD_STRIP);
#ifdef STATISTICS
	STAT_num_of_quad_strips++;
#endif
}

void
OpenGLSurfaceEvaluator::endqstrip(void)
{
    glEnd();
}

/*-------------------------------------------------------------------------
 * bgnmap2f - preamble to surface definition and evaluations
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::bgnmap2f(long)
{

    glPushAttrib((GLbitfield) GL_EVAL_BIT);

}

/*-------------------------------------------------------------------------
 * endmap2f - postamble to a map
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::endmap2f(void)
{

    glPopAttrib();

#ifdef STATISTICS
    fprintf(stderr, "num_vertices=%i,num_triangles=%i,num_quads_strips=%i\n", STAT_num_of_eval_vertices,STAT_num_of_triangles,STAT_num_of_quad_strips);
#endif

}

/*-------------------------------------------------------------------------
 * map2f - pass a desription of a surface map
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::map2f(
    long _type,
    REAL _ulower,	/* u lower domain coord		*/
    REAL _uupper,	/* u upper domain coord 	*/
    long _ustride,	/* interpoint distance		*/
    long _uorder,	/* parametric order		*/
    REAL _vlower,	/* v lower domain coord		*/
    REAL _vupper, 	/* v upper domain coord		*/
    long _vstride,	/* interpoint distance		*/
    long _vorder,	/* parametric order		*/
    REAL *pts) 	/* control points		*/
{
#ifdef USE_INTERNAL_EVAL
   inMap2f((int) _type, (REAL) _ulower, (REAL) _uupper, 
	    (int) _ustride, (int) _uorder, (REAL) _vlower, 
	    (REAL) _vupper, (int) _vstride, (int) _vorder, 
	    (REAL *) pts);
#else
    glMap2f((GLenum) _type, (GLfloat) _ulower, (GLfloat) _uupper, 
	    (GLint) _ustride, (GLint) _uorder, (GLfloat) _vlower, 
	    (GLfloat) _vupper, (GLint) _vstride, (GLint) _vorder, 
	    (const GLfloat *) pts);
#endif
}


/*-------------------------------------------------------------------------
 * mapmesh2f - evaluate a mesh of points on lattice
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::mapmesh2f(long style, long umin, long umax, long vmin, long vmax)
{
#ifdef NO_EVALUATION
	return;
#endif

#ifdef USE_INTERNAL_EVAL
    inEvalMesh2((int)umin, (int)vmin, (int)umax, (int)vmax);
#else
    switch(style) {
    default:
    case N_MESHFILL:

	glEvalMesh2((GLenum) GL_FILL, (GLint) umin, (GLint) umax, 
		(GLint) vmin, (GLint) vmax);
	break;
    case N_MESHLINE:
	glEvalMesh2((GLenum) GL_LINE, (GLint) umin, (GLint) umax, 
		(GLint) vmin, (GLint) vmax);
	break;
    case N_MESHPOINT:
	glEvalMesh2((GLenum) GL_POINT, (GLint) umin, (GLint) umax, 
		(GLint) vmin, (GLint) vmax);
	break;
    }
#endif

#ifdef STATISTICS
	STAT_num_of_quad_strips += (umax-umin)*(vmax-vmin);
#endif
}

/*-------------------------------------------------------------------------
 * evalcoord2f - evaluate a point on a surface
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::evalcoord2f(long, REAL u, REAL v)
{


#ifdef NO_EVALUATION
return;
#endif


    newtmeshvert(u, v);
}

/*-------------------------------------------------------------------------
 * evalpoint2i - evaluate a grid point
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::evalpoint2i(long u, long v)
{
#ifdef NO_EVALUATION
return;
#endif

    newtmeshvert(u, v);
}

void
OpenGLSurfaceEvaluator::point2i( long u, long v )
{
#ifdef USE_INTERNAL_EVAL
    inEvalPoint2( (int)u,  (int)v);
#else
    glEvalPoint2((GLint) u, (GLint) v);
#endif

#ifdef STATISTICS
  STAT_num_of_eval_vertices++;
#endif

}

void
OpenGLSurfaceEvaluator::coord2f( REAL u, REAL v )
{
#ifdef USE_INTERNAL_EVAL
    inEvalCoord2f( u, v);
#else
    glEvalCoord2f((GLfloat) u, (GLfloat) v);
#endif


#ifdef STATISTICS
  STAT_num_of_eval_vertices++;
#endif

}

void
OpenGLSurfaceEvaluator::newtmeshvert( long u, long v )
{
    if (tmeshing) {

	if (vcount == 2) {
	    vertexCache[0]->invoke(this);
	    vertexCache[1]->invoke(this);
	    point2i( u,  v);

	} else {
	    vcount++;
	}

	vertexCache[which]->saveEvalPoint(u, v);
	which = 1 - which;
    } else {
	point2i( u,  v);
    }
}

void
OpenGLSurfaceEvaluator::newtmeshvert( REAL u, REAL v )
{

    if (tmeshing) {


	if (vcount == 2) {
	    vertexCache[0]->invoke(this);
	    vertexCache[1]->invoke(this);
            coord2f(u,v);

	} else {
	    vcount++;
	}

	vertexCache[which]->saveEvalCoord(u, v);
	which = 1 - which;
    } else {

	coord2f( u,  v);
    }
}