/*
 ** License Applicability. Except to the extent portions of this file are
 ** made subject to an alternative license as permitted in the SGI Free
 ** Software License B, Version 1.1 (the "License"), the contents of this
 ** file are subject only to the provisions of the License. You may not use
 ** this file except in compliance with the License. You may obtain a copy
 ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
 **
 ** http://oss.sgi.com/projects/FreeB
 **
 ** Note that, as provided in the License, the Software is distributed on an
 ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
 ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
 ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
 ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
 **
 ** Original Code. The Original Code is: OpenGL Sample Implementation,
 ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
 ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
 ** Copyright in any portions created by third parties is as indicated
 ** elsewhere herein. All Rights Reserved.
 **
 ** Additional Notice Provisions: The application programming interfaces
 ** established by SGI in conjunction with the Original Code are The
 ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
 ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
 ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
 ** Window System(R) (Version 1.3), released October 19, 1998. This software
 ** was created using the OpenGL(R) version 1.2.1 Sample Implementation
 ** published by SGI, but has not been independently verified as being
 ** compliant with the OpenGL(R) version 1.2.1 Specification.
 */

/*
 * glsurfeval.c++
 *
 */

/* Polynomial Evaluator Interface */
#include "gluos.h"
#include <stdio.h>
#include "glimports.h"
#include "glrenderer.h"
#include "glsurfeval.h"
#include "nurbsconsts.h"
#include "bezierPatchMesh.h"


//extern int surfcount;
//int surfcount=0;

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

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

//#define USE_LOD //for LOD test, have to turn on USE_LOD in insurfeval.c++ too

/*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

/*for output triangles*/
/*#define OUTPUT_TRIANGLES*/


/*#define FOR_CHRIS*/
#ifdef FOR_CHRIS
extern "C"  {  void                evalUStripExt(int n_upper, REAL v_upper, REAL* upper_val,
		int n_lower, REAL v_lower, REAL* lower_val);}

extern "C" {   void                evalVStripExt(int n_left, REAL u_left, REAL* left_val,
		int n_right, REAL u_right, REAL* right_val);
}
#endif


/**************begin for LOD_eval_list***********/
void OpenGLSurfaceEvaluator::LOD_eval_list(int level)
{
	if(level == 0)
		LOD_eval_level = 1;
	else if(level == 1)
		LOD_eval_level = 2;
	else if(level == 2)
		LOD_eval_level = 4;
	else
		LOD_eval_level = 8;

	inBPMListEvalEM(global_bpm);
}


OpenGLSurfaceEvaluator::OpenGLSurfaceEvaluator()
{
	int i;

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

	global_uorder = 0;
	global_vorder = 0;
	global_uprime = -1.0;
	global_vprime = -1.0;
	global_vprime_BV = -1.0;
	global_uprime_BU = -1.0;
	global_uorder_BU = 0;
	global_vorder_BU = 0;
	global_uorder_BV = 0;
	global_vorder_BV = 0;
	global_baseData = NULL;

	global_bpm = NULL;
	output_triangles = 0; //don't output triangles by default

	//no default callback functions
	beginCallBackN = NULL;
	endCallBackN = NULL;
	vertexCallBackN = NULL;
	normalCallBackN = NULL;
	colorCallBackN = NULL;
	texcoordCallBackN = NULL;
	beginCallBackData = NULL;
	endCallBackData = NULL;
	vertexCallBackData = NULL;
	normalCallBackData = NULL;
	colorCallBackData = NULL;
	texcoordCallBackData = NULL;

	userData = NULL;

	auto_normal_flag = 0;
	callback_auto_normal = 0; //default of GLU_CALLBACK_AUTO_NORMAL is 0
	vertex_flag = 0;
	normal_flag = 0;
	color_flag = 0;
	texcoord_flag = 0;

	em_vertex.uprime = -1.0;
	em_vertex.vprime = -1.0;
	em_normal.uprime = -1.0;
	em_normal.vprime = -1.0;
	em_color.uprime = -1.0;
	em_color.vprime = -1.0;
	em_texcoord.uprime = -1.0;
	em_texcoord.vprime = -1.0;

#ifdef USE_LOD
	LOD_eval_level = 1;
#endif
}

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)
{
	m_Self->SetState((GLenum) type, GL_FALSE);
}

/*---------------------------------------------------------------------------
 * enable - turn on a map
 *---------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::enable(long type)
{
	m_Self->SetState((GLenum) type, GL_TRUE);
}

/*-------------------------------------------------------------------------
 * 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

	if(output_triangles)  
	{
		global_grid_u0 = u0;
		global_grid_u1 = u1;
		global_grid_nu = nu;
		global_grid_v0 = v0;
		global_grid_v1 = v1;
		global_grid_nv = nv;
	}
	else
		m_Self->GLMapGrid2f((GLint) nu, (GLfloat) u0, (GLfloat) u1, (GLint) nv,
				(GLfloat) v0, (GLfloat) v1);

#endif
}

void
OpenGLSurfaceEvaluator::polymode(long style)
{
	if(! output_triangles)
	{
		switch(style) {
		default:
		case N_MESHFILL:
			m_Self->GLPolygonMode((GLenum) GL_FRONT_AND_BACK, (GLenum) GL_FILL);
			break;
		case N_MESHLINE:
			m_Self->GLPolygonMode((GLenum) GL_FRONT_AND_BACK, (GLenum) GL_LINE);
			break;
		case N_MESHPOINT:
			m_Self->GLPolygonMode((GLenum) GL_FRONT_AND_BACK, (GLenum) GL_POINT);
			break;
		}
	}
}

void
OpenGLSurfaceEvaluator::bgnline(void)
{
	if(output_triangles)
		bezierPatchMeshBeginStrip(global_bpm, GL_LINE_STRIP);
	else
		m_Self->GLBegin((GLenum) GL_LINE_STRIP);
}

void
OpenGLSurfaceEvaluator::endline(void)
{
	if(output_triangles)
		bezierPatchMeshEndStrip(global_bpm);
	else
		m_Self->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)
{
	if(output_triangles)
		bezierPatchMeshBeginStrip(global_bpm, GL_LINE_LOOP);
	else
		m_Self->GLBegin((GLenum) GL_LINE_LOOP);
}

void
OpenGLSurfaceEvaluator::endclosedline(void)
{
	if(output_triangles)
		bezierPatchMeshEndStrip(global_bpm);
	else
		m_Self->GLEnd();
}





void
OpenGLSurfaceEvaluator::bgntmesh(void)
{

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

	if(output_triangles)
		bezierPatchMeshBeginStrip(global_bpm, GL_TRIANGLES);
	else
		m_Self->GLBegin((GLenum) GL_TRIANGLES);

}

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

}

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


	if(output_triangles)
		bezierPatchMeshEndStrip(global_bpm);
	else
		m_Self->GLEnd();
}

void
OpenGLSurfaceEvaluator::bgntfan(void)
{

	if(output_triangles)
		bezierPatchMeshBeginStrip(global_bpm, GL_TRIANGLE_FAN);
	else
		m_Self->GLBegin((GLenum) GL_TRIANGLE_FAN);

}
void
OpenGLSurfaceEvaluator::endtfan(void)
{
	if(output_triangles)
		bezierPatchMeshEndStrip(global_bpm);
	else
		m_Self->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

#ifdef FOR_CHRIS
	evalUStripExt(n_upper, v_upper, upper_val,
			n_lower, v_lower, lower_val);
	return;

#endif
	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

#ifdef FOR_CHRIS
	evalVStripExt(n_left, u_left, left_val,
			n_right, u_right, right_val);
	return;

#endif

	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)
{
	if(output_triangles)
		bezierPatchMeshBeginStrip(global_bpm, GL_QUAD_STRIP);
	else
		m_Self->GLBegin((GLenum) GL_QUAD_STRIP);

#ifdef STATISTICS
	STAT_num_of_quad_strips++;
#endif
}

void
OpenGLSurfaceEvaluator::endqstrip(void)
{
	if(output_triangles)
		bezierPatchMeshEndStrip(global_bpm);
	else
		m_Self->GLEnd();

}

/*-------------------------------------------------------------------------
 * bgnmap2f - preamble to surface definition and evaluations
 *-------------------------------------------------------------------------
 */
void
OpenGLSurfaceEvaluator::bgnmap2f(long)
{
	if(output_triangles)
	{
		/*deallocate the space which may has been
		 *allocated by global_bpm previously
		 */
		if(global_bpm != NULL) {
			bezierPatchMeshListDelete(global_bpm);
			global_bpm = NULL;
		}


		/*
	auto_normal_flag = 1; //always output normal in callback mode.
			      //we could have used the following code,
			      //but Inspector doesn't have gl context
			      //before it calls tessellator.
			      //this way is temporary.
		 */
		//NEWCALLBACK
		//if one of the two normal callback functions are set,
		//then set
		if(normalCallBackN != NULL ||
				normalCallBackData != NULL)
			auto_normal_flag = 1;
		else
			auto_normal_flag = 0;

		//initialize so that no maps initially
		vertex_flag = 0;
		normal_flag = 0;
		color_flag = 0;
		texcoord_flag = 0;

		/*
      if(glIsEnabled(GL_AUTO_NORMAL) == GL_TRUE)
	auto_normal_flag = 1;
      else if (callback_auto_normal == 1)
	auto_normal_flag = 1;
      else
	auto_normal_flag = 0;
		 */
		m_Self->GLPushAttrib((GLbitfield) GL_EVAL_BIT);

	}
	else
	{
		m_Self->GLPushAttrib((GLbitfield) GL_EVAL_BIT);

		/*to avoid side effect, we restor the opengl state for GL_POLYGON_MODE
		 */       
		m_Self->GLGetIntegerv(GL_POLYGON_MODE, gl_polygon_mode);
	}

}

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

	if(output_triangles)
	{
		//bezierPatchMeshListDelDeg(global_bpm);

		//    bezierPatchMeshListEval(global_bpm);

		//surfcount++;
		//printf("surfcount=%i\n", surfcount);
		//if(surfcount == 8) exit(0);

		inBPMListEvalEM(global_bpm);



		/*
    global_bpm = bezierPatchMeshListReverse(global_bpm);
    {
      float *vertex_array;
      float *normal_array;
      int *length_array;
      int *type_array;
      int num_strips;
      bezierPatchMeshListCollect(global_bpm, &vertex_array, &normal_array, &length_array, &type_array, &num_strips);
      drawStrips(vertex_array, normal_array, length_array, type_array, num_strips);
      free(vertex_array);
      free(normal_array);
      free(length_array);
      free(type_array);
    }
		 */

		//bezierPatchMeshListPrint(global_bpm);
		//bezierPatchMeshListDraw(global_bpm);

		//	  printf("num triangles=%i\n", bezierPatchMeshListNumTriangles(global_bpm));

#ifdef USE_LOD
#else
		bezierPatchMeshListDelete(global_bpm);
		global_bpm = NULL;
#endif
		m_Self->GLPopAttrib();
	}
	else
	{
#ifndef USE_LOD
		m_Self->GLPopAttrib();
#endif

#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

		/*to restore the gl_polygon_mode
		 */
#ifndef USE_LOD
		m_Self->GLPolygonMode( GL_FRONT, (GLenum) gl_polygon_mode[0]);
		m_Self->GLPolygonMode( GL_BACK,  (GLenum) gl_polygon_mode[1]);
#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



	if(output_triangles)
	{
		if(global_bpm == NULL)
			global_bpm = bezierPatchMeshMake2(10,10);
		if(
				(global_bpm->bpatch == NULL &&
						(_type == GL_MAP2_VERTEX_3 || _type == GL_MAP2_VERTEX_4))
						||
						(global_bpm->bpatch_normal == NULL &&
								(_type == GL_MAP2_NORMAL))
								||
								(global_bpm->bpatch_color == NULL &&
										(_type == GL_MAP2_INDEX || _type == GL_MAP2_COLOR_4))
										||
										(global_bpm->bpatch_texcoord == NULL &&
												(_type == GL_MAP2_TEXTURE_COORD_1 ||
														_type == GL_MAP2_TEXTURE_COORD_2 ||
														_type == GL_MAP2_TEXTURE_COORD_3 ||
														_type == GL_MAP2_TEXTURE_COORD_4 )
										))
		{
			bezierPatchMeshPutPatch(global_bpm, (int) _type, _ulower, _uupper,(int)  _ustride,(int) _uorder,_vlower, _vupper, (int) _vstride, (int) _vorder, pts);
		}
		else /*new surface patch (with multiple maps) starts*/
		{
			bezierPatchMesh *temp = bezierPatchMeshMake2(10,10);
			bezierPatchMeshPutPatch(temp, (int) _type, _ulower, _uupper,(int)  _ustride,(int) _uorder,_vlower, _vupper, (int) _vstride, (int) _vorder, pts);
			global_bpm = bezierPatchMeshListInsert(global_bpm, temp);

			/*
	   global_bpm = bezierPatchMeshListInsert(global_bpm,
						  bezierPatchMeshMake(
								      (int) _type, _ulower, _uupper,(int)  _ustride, (int) _uorder, _vlower, _vupper, (int) _vstride, (int) _vorder, pts, 10, 10));
			 */
		}
	}
	else /*not output triangles*/
	{
		m_Self->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



	if(output_triangles)
	{
#ifdef USE_LOD
		bezierPatchMeshBeginStrip(global_bpm, GL_POLYGON);
		bezierPatchMeshInsertUV(global_bpm, global_grid_u0, global_grid_v0);
		bezierPatchMeshInsertUV(global_bpm, global_grid_u1, global_grid_v1);
		bezierPatchMeshInsertUV(global_bpm, (REAL)global_grid_nu, (REAL)global_grid_nv);
		bezierPatchMeshInsertUV(global_bpm, (REAL)umin, (REAL)vmin);
		bezierPatchMeshInsertUV(global_bpm, (REAL)umax, (REAL)vmax);
		bezierPatchMeshEndStrip(global_bpm);

#else

		REAL du, dv;
		long i,j;
		if(global_grid_nu == 0 || global_grid_nv == 0)
			return; /*no points need to be output*/
		du = (global_grid_u1 - global_grid_u0) / (REAL)global_grid_nu;
		dv = (global_grid_v1 - global_grid_v0) / (REAL)global_grid_nv;

		if(global_grid_nu >= global_grid_nv){

			for(i=umin; i<umax; i++){
				REAL u1 = (i==global_grid_nu)? global_grid_u1:(global_grid_u0 + i*du);
				REAL u2 = ((i+1) == global_grid_nu)? global_grid_u1: (global_grid_u0+(i+1)*du);

				bgnqstrip();
				for(j=vmax; j>=vmin; j--){
					REAL v1 = (j == global_grid_nv)? global_grid_v1: (global_grid_v0 +j*dv);

					coord2f(u1, v1);
					coord2f(u2, v1);
				}
				endqstrip();
			}
		}
		else{

			for(i=vmin; i<vmax; i++){
				REAL v1 = (i==global_grid_nv)? global_grid_v1:(global_grid_v0 + i*dv);
				REAL v2 = ((i+1) == global_grid_nv)? global_grid_v1: (global_grid_v0+(i+1)*dv);

				bgnqstrip();
				for(j=umax; j>=umin; j--){
					REAL u1 = (j == global_grid_nu)? global_grid_u1: (global_grid_u0 +j*du);        
					coord2f(u1, v2);
					coord2f(u1, v1);
				}
				endqstrip();
			}
		}
#endif
	}
	else
	{
		switch(style) {
		default:
		case N_MESHFILL:
			m_Self->GLEvalMesh2((GLenum) GL_FILL, (GLint) umin, (GLint) umax,
					(GLint) vmin, (GLint) vmax);
			break;
		case N_MESHLINE:
			m_Self->GLEvalMesh2((GLenum) GL_LINE, (GLint) umin, (GLint) umax,
					(GLint) vmin, (GLint) vmax);
			break;
		case N_MESHPOINT:
			m_Self->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 NO_EVALUATION
	return;
#else

#ifdef USE_INTERNAL_EVAL
	inEvalPoint2( (int)u,  (int)v);
#else


	if(output_triangles)
	{

		REAL du, dv;
		REAL fu,fv;
		du = (global_grid_u1 - global_grid_u0) / (REAL)global_grid_nu;
		dv = (global_grid_v1 - global_grid_v0) / (REAL)global_grid_nv;
		fu = (u==global_grid_nu)? global_grid_u1:(global_grid_u0 + u*du);
		fv = (v == global_grid_nv)? global_grid_v1: (global_grid_v0 +v*dv);
		coord2f(fu,fv);
	}
	else
		m_Self->GLEvalPoint2((GLint) u, (GLint) v);


#endif

#ifdef STATISTICS
	STAT_num_of_eval_vertices++;
#endif

#endif

}

void
OpenGLSurfaceEvaluator::coord2f( REAL u, REAL v )
{
#ifdef NO_EVALUATION
	return;
#else

#ifdef USE_INTERNAL_EVAL
	inEvalCoord2f( u, v);
#else


	if(output_triangles)
		bezierPatchMeshInsertUV(global_bpm, u,v);
	else
		m_Self->GLEvalCoord2f((GLfloat) u, (GLfloat) v);


#endif


#ifdef STATISTICS
	STAT_num_of_eval_vertices++;
#endif

#endif
}

void
OpenGLSurfaceEvaluator::newtmeshvert( long u, long v )
{
#ifdef NO_EVALUATION
	return;
#else

	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);
	}
#endif
}

void
OpenGLSurfaceEvaluator::newtmeshvert( REAL u, REAL v )
{
#ifdef NO_EVALUATION
	return;
#else
	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);
	}
#endif

}

void
OpenGLSurfaceEvaluator::putCallBack(GLenum which, _GLUfuncptr fn )
{
	switch(which)
	{
	case GLU_NURBS_BEGIN:
		beginCallBackN = (void (GLAPIENTRY *) (GLenum)) fn;
		break;
	case GLU_NURBS_END:
		endCallBackN = (void (GLAPIENTRY *) (void)) fn;
		break;
	case GLU_NURBS_VERTEX:
		vertexCallBackN = (void (GLAPIENTRY *) (const GLfloat*)) fn;
		break;
	case GLU_NURBS_NORMAL:
		normalCallBackN = (void (GLAPIENTRY *) (const GLfloat*)) fn;
		break;
	case GLU_NURBS_COLOR:
		colorCallBackN = (void (GLAPIENTRY *) (const GLfloat*)) fn;
		break;
	case GLU_NURBS_TEXTURE_COORD:
		texcoordCallBackN = (void (GLAPIENTRY *) (const GLfloat*)) fn;
		break;
	case GLU_NURBS_BEGIN_DATA:
		beginCallBackData = (void (GLAPIENTRY *) (GLenum, void*)) fn;
		break;
	case GLU_NURBS_END_DATA:
		endCallBackData = (void (GLAPIENTRY *) (void*)) fn;
		break;
	case GLU_NURBS_VERTEX_DATA:
		vertexCallBackData = (void (GLAPIENTRY *) (const GLfloat*, void*)) fn;
		break;
	case GLU_NURBS_NORMAL_DATA:
		normalCallBackData = (void (GLAPIENTRY *) (const GLfloat*, void*)) fn;
		break;
	case GLU_NURBS_COLOR_DATA:
		colorCallBackData = (void (GLAPIENTRY *) (const GLfloat*, void*)) fn;
		break;
	case GLU_NURBS_TEXTURE_COORD_DATA:
		texcoordCallBackData = (void (GLAPIENTRY *) (const GLfloat*, void*)) fn;
		break;

	}
}


void
OpenGLSurfaceEvaluator::beginCallBack(GLenum which, void *data)
{
	if(beginCallBackData)
		beginCallBackData(which, data);
	else if(beginCallBackN)
		beginCallBackN(which);
}

void
OpenGLSurfaceEvaluator::endCallBack(void *data)
{
	if(endCallBackData)
		endCallBackData(data);
	else if(endCallBackN)
		endCallBackN();
}

void
OpenGLSurfaceEvaluator::vertexCallBack(const GLfloat *vert, void* data)
{
	if(vertexCallBackData)
		vertexCallBackData(vert, data);
	else if(vertexCallBackN)
		vertexCallBackN(vert);
}


void
OpenGLSurfaceEvaluator::normalCallBack(const GLfloat *normal, void* data)
{
	if(normalCallBackData)
		normalCallBackData(normal, data);
	else if(normalCallBackN)
		normalCallBackN(normal);
}

void
OpenGLSurfaceEvaluator::colorCallBack(const GLfloat *color, void* data)
{
	if(colorCallBackData)
		colorCallBackData(color, data);
	else if(colorCallBackN)
		colorCallBackN(color);
}

void
OpenGLSurfaceEvaluator::texcoordCallBack(const GLfloat *texcoord, void* data)
{
	if(texcoordCallBackData)
		texcoordCallBackData(texcoord, data);
	else if(texcoordCallBackN)
		texcoordCallBackN(texcoord);
}




