/* $Id: eval.c,v 1.2 2000/10/27 15:21:40 mholst Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  2.0
 * Copyright (C) 1995-1996  Brian Paul
 *
 * 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.
 */

/*
 * eval.c was written by
 * Bernd Barsuhn (bdbarsuh@cip.informatik.uni-erlangen.de) and
 * Volker Weiss (vrweiss@cip.informatik.uni-erlangen.de).
 *
 * My original implementation of evaluators was simplistic and didn't
 * compute surface normal vectors properly.  Bernd and Volker applied
 * used more sophisticated methods to get better results.
 *
 * Thanks guys!
 */



#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "context.h"
#include "draw.h"
#include "eval.h"
#include "dlist.h"
#include "macros.h"
#include "types.h"



/*
 * Horner scheme for Bezier curves
 * 
 * Bezier curves can be computed via a Horner scheme.
 * Horner is numerically less stable than the de Casteljau
 * algorithm, but it is faster. For curves of degree n 
 * the complexity of Horner is O(n) and de Casteljau is O(n^2).
 * Since stability is not important for displaying curve 
 * points I decided to use the Horner scheme.
 *
 * A cubic Bezier curve with control points b0, b1, b2, b3 can be 
 * written as
 *
 *        (([3]        [3]     )     [3]       )     [3]
 * c(t) = (([0]*s*b0 + [1]*t*b1)*s + [2]*t^2*b2)*s + [3]*t^2*b3
 *
 *                                           [n]
 * where s=1-t and the binomial coefficients [i]. These can 
 * be computed iteratively using the identity:
 *
 * [n]               [n  ]             [n]
 * [i] = (n-i+1)/i * [i-1]     and     [0] = 1
 */

static void
horner_bezier_curve(GLfloat *cp, GLfloat *out, GLfloat t,
                    GLuint dim, GLuint order)
{
  GLfloat s, powert;
  GLuint i, k, bincoeff;

  if(order >= 2)
  { 
    bincoeff = order-1;
    s = 1.0-t;

    for(k=0; k<dim; k++)
      out[k] = s*cp[k] + bincoeff*t*cp[dim+k];

    for(i=2, cp+=2*dim, powert=t*t; i<order; i++, powert*=t, cp +=dim)
    {
      bincoeff *= order-i;
      bincoeff /= i;

      for(k=0; k<dim; k++)
        out[k] = s*out[k] + bincoeff*powert*cp[k];
    }
  }
  else /* order=1 -> constant curve */
  { 
    for(k=0; k<dim; k++)
      out[k] = cp[k];
  } 
}

/*
 * Tensor product Bezier surfaces
 *
 * Again the Horner scheme is used to compute a point on a 
 * TP Bezier surface. First a control polygon for a curve
 * on the surface in one parameter direction is computed,
 * then the point on the curve for the other parameter 
 * direction is evaluated.
 *
 * To store the curve control polygon additional storage
 * for max(uorder,vorder) points is needed in the 
 * control net cn.
 */

static void
horner_bezier_surf(GLfloat *cn, GLfloat *out, GLfloat u, GLfloat v,
                   GLuint dim, GLuint uorder, GLuint vorder)
{
  GLfloat *cp = cn + uorder*vorder*dim;
  GLuint i, uinc = vorder*dim;

  if(vorder > uorder)
  {
    if(uorder >= 2)
    { 
      GLfloat s, poweru;
      GLuint j, k, bincoeff;

      /* Compute the control polygon for the surface-curve in u-direction */
      for(j=0; j<vorder; j++)
      {
        GLfloat *ucp = &cn[j*dim];

        /* Each control point is the point for parameter u on a */ 
        /* curve defined by the control polygons in u-direction */
	bincoeff = uorder-1;
	s = 1.0-u;

	for(k=0; k<dim; k++)
	  cp[j*dim+k] = s*ucp[k] + bincoeff*u*ucp[uinc+k];

	for(i=2, ucp+=2*uinc, poweru=u*u; i<uorder; 
            i++, poweru*=u, ucp +=uinc)
	{
	  bincoeff *= uorder-i;
          bincoeff /= i;

	  for(k=0; k<dim; k++)
	    cp[j*dim+k] = s*cp[j*dim+k] + bincoeff*poweru*ucp[k];
	}
      }
        
      /* Evaluate curve point in v */
      horner_bezier_curve(cp, out, v, dim, vorder);
    }
    else /* uorder=1 -> cn defines a curve in v */
      horner_bezier_curve(cn, out, v, dim, vorder);
  }
  else /* vorder <= uorder */
  {
    if(vorder > 1)
    {
      GLuint i;

      /* Compute the control polygon for the surface-curve in u-direction */
      for(i=0; i<uorder; i++, cn += uinc)
      {
	/* For constant i all cn[i][j] (j=0..vorder) are located */
	/* on consecutive memory locations, so we can use        */
	/* horner_bezier_curve to compute the control points     */

	horner_bezier_curve(cn, &cp[i*dim], v, dim, vorder);
      }

      /* Evaluate curve point in u */
      horner_bezier_curve(cp, out, u, dim, uorder);
    }
    else  /* vorder=1 -> cn defines a curve in u */
      horner_bezier_curve(cn, out, u, dim, uorder);
  }
}

/*
 * The direct de Casteljau algorithm is used when a point on the
 * surface and the tangent directions spanning the tangent plane
 * should be computed (this is needed to compute normals to the
 * surface). In this case the de Casteljau algorithm approach is
 * nicer because a point and the partial derivatives can be computed 
 * at the same time. To get the correct tangent length du and dv
 * must be multiplied with the (u2-u1)/uorder-1 and (v2-v1)/vorder-1. 
 * Since only the directions are needed, this scaling step is omitted.
 *
 * De Casteljau needs additional storage for uorder*vorder
 * values in the control net cn.
 */

static void
de_casteljau_surf(GLfloat *cn, GLfloat *out, GLfloat *du, GLfloat *dv,
                  GLfloat u, GLfloat v, GLuint dim, 
                  GLuint uorder, GLuint vorder)
{
  GLfloat *dcn = cn + uorder*vorder*dim;
  GLfloat us = 1.0-u, vs = 1.0-v;
  GLuint h, i, j, k;
  GLuint minorder = uorder < vorder ? uorder : vorder;
  GLuint uinc = vorder*dim;
  GLuint dcuinc = vorder;
 
  /* Each component is evaluated separately to save buffer space  */
  /* This does not drasticaly decrease the performance of the     */
  /* algorithm. If additional storage for (uorder-1)*(vorder-1)   */
  /* points would be available, the components could be accessed  */
  /* in the innermost loop which could lead to less cache misses. */

#define CN(I,J,K) cn[(I)*uinc+(J)*dim+(K)] 
#define DCN(I, J) dcn[(I)*dcuinc+(J)]
  if(minorder < 3)
  {
    if(uorder==vorder)
    {
      for(k=0; k<dim; k++)
      {
	/* Derivative direction in u */
	du[k] = vs*(CN(1,0,k) - CN(0,0,k)) +
	         v*(CN(1,1,k) - CN(0,1,k));

	/* Derivative direction in v */
	dv[k] = us*(CN(0,1,k) - CN(0,0,k)) + 
	         u*(CN(1,1,k) - CN(1,0,k));

	/* bilinear de Casteljau step */
        out[k] =  us*(vs*CN(0,0,k) + v*CN(0,1,k)) +
	           u*(vs*CN(1,0,k) + v*CN(1,1,k));
      }
    }
    else if(minorder == uorder)
    {
      for(k=0; k<dim; k++)
      {
	/* bilinear de Casteljau step */
	DCN(1,0) =    CN(1,0,k) -   CN(0,0,k);
	DCN(0,0) = us*CN(0,0,k) + u*CN(1,0,k);

	for(j=0; j<vorder-1; j++)
	{
	  /* for the derivative in u */
	  DCN(1,j+1) =    CN(1,j+1,k) -   CN(0,j+1,k);
	  DCN(1,j)   = vs*DCN(1,j)    + v*DCN(1,j+1);

	  /* for the `point' */
	  DCN(0,j+1) = us*CN(0,j+1,k) + u*CN(1,j+1,k);
	  DCN(0,j)   = vs*DCN(0,j)    + v*DCN(0,j+1);
	}
        
	/* remaining linear de Casteljau steps until the second last step */
	for(h=minorder; h<vorder-1; h++)
	  for(j=0; j<vorder-h; j++)
	  {
	    /* for the derivative in u */
	    DCN(1,j) = vs*DCN(1,j) + v*DCN(1,j+1);

	    /* for the `point' */
	    DCN(0,j) = vs*DCN(0,j) + v*DCN(0,j+1);
	  }

	/* derivative direction in v */
	dv[k] = DCN(0,1) - DCN(0,0);

	/* derivative direction in u */
	du[k] =   vs*DCN(1,0) + v*DCN(1,1);

	/* last linear de Casteljau step */
	out[k] =  vs*DCN(0,0) + v*DCN(0,1);
      }
    }
    else /* minorder == vorder */
    {
      for(k=0; k<dim; k++)
      {
	/* bilinear de Casteljau step */
	DCN(0,1) =    CN(0,1,k) -   CN(0,0,k);
	DCN(0,0) = vs*CN(0,0,k) + v*CN(0,1,k);
	for(i=0; i<uorder-1; i++)
	{
	  /* for the derivative in v */
	  DCN(i+1,1) =    CN(i+1,1,k) -   CN(i+1,0,k);
	  DCN(i,1)   = us*DCN(i,1)    + u*DCN(i+1,1);

	  /* for the `point' */
	  DCN(i+1,0) = vs*CN(i+1,0,k) + v*CN(i+1,1,k);
	  DCN(i,0)   = us*DCN(i,0)    + u*DCN(i+1,0);
	}
        
	/* remaining linear de Casteljau steps until the second last step */
	for(h=minorder; h<uorder-1; h++)
	  for(i=0; i<uorder-h; i++)
	  {
	    /* for the derivative in v */
	    DCN(i,1) = us*DCN(i,1) + u*DCN(i+1,1);

	    /* for the `point' */
	    DCN(i,0) = us*DCN(i,0) + u*DCN(i+1,0);
	  }

	/* derivative direction in u */
	du[k] = DCN(1,0) - DCN(0,0);

	/* derivative direction in v */
	dv[k] =   us*DCN(0,1) + u*DCN(1,1);

	/* last linear de Casteljau step */
	out[k] =  us*DCN(0,0) + u*DCN(1,0);
      }
    }
  }
  else if(uorder == vorder)
  {
    for(k=0; k<dim; k++)
    {
      /* first bilinear de Casteljau step */
      for(i=0; i<uorder-1; i++)
      {
	DCN(i,0) = us*CN(i,0,k) + u*CN(i+1,0,k);
	for(j=0; j<vorder-1; j++)
	{
	  DCN(i,j+1) = us*CN(i,j+1,k) + u*CN(i+1,j+1,k);
	  DCN(i,j)   = vs*DCN(i,j)    + v*DCN(i,j+1);
	}
      }

      /* remaining bilinear de Casteljau steps until the second last step */
      for(h=2; h<minorder-1; h++)
	for(i=0; i<uorder-h; i++)
	{
	  DCN(i,0) = us*DCN(i,0) + u*DCN(i+1,0);
	  for(j=0; j<vorder-h; j++)
	  {
	    DCN(i,j+1) = us*DCN(i,j+1) + u*DCN(i+1,j+1);
	    DCN(i,j)   = vs*DCN(i,j)   + v*DCN(i,j+1);
	  }
	}

      /* derivative direction in u */
      du[k] = vs*(DCN(1,0) - DCN(0,0)) +
	       v*(DCN(1,1) - DCN(0,1));

      /* derivative direction in v */
      dv[k] = us*(DCN(0,1) - DCN(0,0)) + 
	       u*(DCN(1,1) - DCN(1,0));

      /* last bilinear de Casteljau step */
      out[k] =  us*(vs*DCN(0,0) + v*DCN(0,1)) +
	         u*(vs*DCN(1,0) + v*DCN(1,1));
    }
  }
  else if(minorder == uorder)
  {
    for(k=0; k<dim; k++)
    {
      /* first bilinear de Casteljau step */
      for(i=0; i<uorder-1; i++)
      {
	DCN(i,0) = us*CN(i,0,k) + u*CN(i+1,0,k);
	for(j=0; j<vorder-1; j++)
	{
	  DCN(i,j+1) = us*CN(i,j+1,k) + u*CN(i+1,j+1,k);
	  DCN(i,j)   = vs*DCN(i,j)    + v*DCN(i,j+1);
	}
      }

      /* remaining bilinear de Casteljau steps until the second last step */
      for(h=2; h<minorder-1; h++)
	for(i=0; i<uorder-h; i++)
	{
	  DCN(i,0) = us*DCN(i,0) + u*DCN(i+1,0);
	  for(j=0; j<vorder-h; j++)
	  {
	    DCN(i,j+1) = us*DCN(i,j+1) + u*DCN(i+1,j+1);
	    DCN(i,j)   = vs*DCN(i,j)   + v*DCN(i,j+1);
	  }
	}

      /* last bilinear de Casteljau step */
      DCN(2,0) =    DCN(1,0) -   DCN(0,0);
      DCN(0,0) = us*DCN(0,0) + u*DCN(1,0);
      for(j=0; j<vorder-1; j++)
      {
	/* for the derivative in u */
	DCN(2,j+1) =    DCN(1,j+1) -    DCN(0,j+1);
	DCN(2,j)   = vs*DCN(2,j)    + v*DCN(2,j+1);
	
	/* for the `point' */
	DCN(0,j+1) = us*DCN(0,j+1 ) + u*DCN(1,j+1);
	DCN(0,j)   = vs*DCN(0,j)    + v*DCN(0,j+1);
      }
        
      /* remaining linear de Casteljau steps until the second last step */
      for(h=minorder; h<vorder-1; h++)
	for(j=0; j<vorder-h; j++)
	{
	  /* for the derivative in u */
	  DCN(2,j) = vs*DCN(2,j) + v*DCN(2,j+1);
	  
	  /* for the `point' */
	  DCN(0,j) = vs*DCN(0,j) + v*DCN(0,j+1);
	}
      
      /* derivative direction in v */
      dv[k] = DCN(0,1) - DCN(0,0);
      
      /* derivative direction in u */
      du[k] =   vs*DCN(2,0) + v*DCN(2,1);
      
      /* last linear de Casteljau step */
      out[k] =  vs*DCN(0,0) + v*DCN(0,1);
    }
  }
  else /* minorder == vorder */
  {
    for(k=0; k<dim; k++)
    {
      /* first bilinear de Casteljau step */
      for(i=0; i<uorder-1; i++)
      {
	DCN(i,0) = us*CN(i,0,k) + u*CN(i+1,0,k);
	for(j=0; j<vorder-1; j++)
	{
	  DCN(i,j+1) = us*CN(i,j+1,k) + u*CN(i+1,j+1,k);
	  DCN(i,j)   = vs*DCN(i,j)    + v*DCN(i,j+1);
	}
      }

      /* remaining bilinear de Casteljau steps until the second last step */
      for(h=2; h<minorder-1; h++)
	for(i=0; i<uorder-h; i++)
	{
	  DCN(i,0) = us*DCN(i,0) + u*DCN(i+1,0);
	  for(j=0; j<vorder-h; j++)
	  {
	    DCN(i,j+1) = us*DCN(i,j+1) + u*DCN(i+1,j+1);
	    DCN(i,j)   = vs*DCN(i,j)   + v*DCN(i,j+1);
	  }
	}

      /* last bilinear de Casteljau step */
      DCN(0,2) =    DCN(0,1) -   DCN(0,0);
      DCN(0,0) = vs*DCN(0,0) + v*DCN(0,1);
      for(i=0; i<uorder-1; i++)
      {
	/* for the derivative in v */
	DCN(i+1,2) =    DCN(i+1,1)  -   DCN(i+1,0);
	DCN(i,2)   = us*DCN(i,2)    + u*DCN(i+1,2);
	
	/* for the `point' */
	DCN(i+1,0) = vs*DCN(i+1,0)  + v*DCN(i+1,1);
	DCN(i,0)   = us*DCN(i,0)    + u*DCN(i+1,0);
      }
      
      /* remaining linear de Casteljau steps until the second last step */
      for(h=minorder; h<uorder-1; h++)
	for(i=0; i<uorder-h; i++)
	{
	  /* for the derivative in v */
	  DCN(i,2) = us*DCN(i,2) + u*DCN(i+1,2);
	  
	  /* for the `point' */
	  DCN(i,0) = us*DCN(i,0) + u*DCN(i+1,0);
	}
      
      /* derivative direction in u */
      du[k] = DCN(1,0) - DCN(0,0);
      
      /* derivative direction in v */
      dv[k] =   us*DCN(0,2) + u*DCN(1,2);
      
      /* last linear de Casteljau step */
      out[k] =  us*DCN(0,0) + u*DCN(1,0);
    }
  }
#undef DCN
#undef CN
}

/*
 * Return the number of components per control point for any type of
 * evaluator.  Return 0 if bad target.
 */

static GLint components( GLenum target )
{
   switch (target) {
      case GL_MAP1_VERTEX_3:		return 3;
      case GL_MAP1_VERTEX_4:		return 4;
      case GL_MAP1_INDEX:		return 1;
      case GL_MAP1_COLOR_4:		return 4;
      case GL_MAP1_NORMAL:		return 3;
      case GL_MAP1_TEXTURE_COORD_1:	return 1;
      case GL_MAP1_TEXTURE_COORD_2:	return 2;
      case GL_MAP1_TEXTURE_COORD_3:	return 3;
      case GL_MAP1_TEXTURE_COORD_4:	return 4;
      case GL_MAP2_VERTEX_3:		return 3;
      case GL_MAP2_VERTEX_4:		return 4;
      case GL_MAP2_INDEX:		return 1;
      case GL_MAP2_COLOR_4:		return 4;
      case GL_MAP2_NORMAL:		return 3;
      case GL_MAP2_TEXTURE_COORD_1:	return 1;
      case GL_MAP2_TEXTURE_COORD_2:	return 2;
      case GL_MAP2_TEXTURE_COORD_3:	return 3;
      case GL_MAP2_TEXTURE_COORD_4:	return 4;
      default:				return 0;
   }
}



/*
 * Do one-time initialization for evaluators.
 */

void gl_init_eval( GLcontext* ctx )
{
  static int init_flag = 0;

  /* Compute a table of nCr (combination) values used by the
   * Bernstein polynomial generator.
   */

  if (init_flag==0) 
  { /* no initialization needed */ 
  }

  init_flag = 1;
}



/**********************************************************************/
/***            Copy and deallocate control points                  ***/
/**********************************************************************/


/*
 * Copy 1-parametric evaluator control points from user-specified 
 * memory space to a buffer of contiguous control points.
 * Input:  see glMap1f for details
 * Return:  pointer to buffer of contiguous control points or NULL if out
 *          of memory.
 */
GLfloat *gl_copy_map_points1f( GLenum target,
                               GLint ustride, GLint uorder,
                               const GLfloat *points )
{
   GLfloat *buffer, *p;
   GLuint i, k, size = components(target);

   if (!points || components==0) {
      return NULL;
   }

   buffer = (GLfloat *) malloc(uorder * size * sizeof(GLfloat));

   if(buffer) 
      for(i=0, p=buffer; i<uorder; i++, points+=ustride)
	for(k=0; k<size; k++)
	  *p++ = points[k];

   return buffer;
}



/*
 * Same as above but convert doubles to floats.
 */
GLfloat *gl_copy_map_points1d( GLenum target,
			        GLint ustride, GLint uorder,
			        const GLdouble *points )
{
   GLfloat *buffer, *p;
   GLuint i, k, size = components(target);

   buffer = (GLfloat *) malloc(uorder * size * sizeof(GLfloat));

   if(buffer)
      for(i=0, p=buffer; i<uorder; i++, points+=ustride)
	for(k=0; k<size; k++)
	  *p++ = (GLfloat) points[k];

   return buffer;
}



/*
 * Copy 2-parametric evaluator control points from user-specified 
 * memory space to a buffer of contiguous control points.
 * Additional memory is allocated to be used by the horner and
 * de Casteljau evaluation schemes.
 *
 * Input:  see glMap2f for details
 * Return:  pointer to buffer of contiguous control points or NULL if out
 *          of memory.
 */
GLfloat *gl_copy_map_points2f( GLenum target,
			        GLint ustride, GLint uorder,
			        GLint vstride, GLint vorder,
			        const GLfloat *points )
{
   GLfloat *buffer, *p;
   GLuint i, j, k, size, dsize, hsize;
   GLint uinc;

   size = components(target);

   /* max(uorder, vorder) additional points are used in      */
   /* horner evaluation and uorder*vorder additional */
   /* values are needed for de Casteljau                     */
   dsize = (uorder == 2 && vorder == 2)? 0 : uorder*vorder;
   hsize = (uorder > vorder ? uorder : vorder)*size;

   if(hsize>dsize)
     buffer = (GLfloat *) malloc((uorder*vorder*size+hsize)*sizeof(GLfloat));
   else
     buffer = (GLfloat *) malloc((uorder*vorder*size+dsize)*sizeof(GLfloat));

   /* compute the increment value for the u-loop */
   uinc = ustride - vorder*vstride;

   if (buffer) 
      for (i=0, p=buffer; i<uorder; i++, points += uinc)
	 for (j=0; j<vorder; j++, points += vstride)
	    for (k=0; k<size; k++)
	       *p++ = points[k];

   return buffer;
}



/*
 * Same as above but convert doubles to floats.
 */
GLfloat *gl_copy_map_points2d(GLenum target,
                              GLint ustride, GLint uorder,
                              GLint vstride, GLint vorder,
                              const GLdouble *points )
{
   GLfloat *buffer, *p;
   GLuint i, j, k, size, hsize, dsize;
   GLint uinc;

   size = components(target);

   /* max(uorder, vorder) additional points are used in      */
   /* horner evaluation and uorder*vorder additional */
   /* values are needed for de Casteljau                     */
   dsize = (uorder == 2 && vorder == 2)? 0 : uorder*vorder;
   hsize = (uorder > vorder ? uorder : vorder)*size;

   if(hsize>dsize)
     buffer = (GLfloat *) malloc((uorder*vorder*size+hsize)*sizeof(GLfloat));
   else
     buffer = (GLfloat *) malloc((uorder*vorder*size+dsize)*sizeof(GLfloat));

   /* compute the increment value for the u-loop */
   uinc = ustride - vorder*vstride;

   if (buffer) 
      for (i=0, p=buffer; i<uorder; i++, points += uinc)
	 for (j=0; j<vorder; j++, points += vstride)
	    for (k=0; k<size; k++)
	       *p++ = (GLfloat) points[k];

   return buffer;
}


/*
 * This function is called by the display list deallocator function to
 * specify that a given set of control points are no longer needed.
 */
void gl_free_control_points( GLcontext* ctx, GLenum target, GLfloat *data )
{
   struct gl_1d_map *map1 = NULL;
   struct gl_2d_map *map2 = NULL;

   switch (target) {
      case GL_MAP1_VERTEX_3:
         map1 = &ctx->EvalMap.Map1Vertex3;
         break;
      case GL_MAP1_VERTEX_4:
         map1 = &ctx->EvalMap.Map1Vertex4;
	 break;
      case GL_MAP1_INDEX:
         map1 = &ctx->EvalMap.Map1Index;
         break;
      case GL_MAP1_COLOR_4:
         map1 = &ctx->EvalMap.Map1Color4;
         break;
      case GL_MAP1_NORMAL:
         map1 = &ctx->EvalMap.Map1Normal;
	 break;
      case GL_MAP1_TEXTURE_COORD_1:
         map1 = &ctx->EvalMap.Map1Texture1;
	 break;
      case GL_MAP1_TEXTURE_COORD_2:
         map1 = &ctx->EvalMap.Map1Texture2;
	 break;
      case GL_MAP1_TEXTURE_COORD_3:
         map1 = &ctx->EvalMap.Map1Texture3;
	 break;
      case GL_MAP1_TEXTURE_COORD_4:
         map1 = &ctx->EvalMap.Map1Texture4;
	 break;
      case GL_MAP2_VERTEX_3:
         map2 = &ctx->EvalMap.Map2Vertex3;
	 break;
      case GL_MAP2_VERTEX_4:
         map2 = &ctx->EvalMap.Map2Vertex4;
	 break;
      case GL_MAP2_INDEX:
         map2 = &ctx->EvalMap.Map2Index;
	 break;
      case GL_MAP2_COLOR_4:
         map2 = &ctx->EvalMap.Map2Color4;
         break;
      case GL_MAP2_NORMAL:
         map2 = &ctx->EvalMap.Map2Normal;
	 break;
      case GL_MAP2_TEXTURE_COORD_1:
         map2 = &ctx->EvalMap.Map2Texture1;
	 break;
      case GL_MAP2_TEXTURE_COORD_2:
         map2 = &ctx->EvalMap.Map2Texture2;
	 break;
      case GL_MAP2_TEXTURE_COORD_3:
         map2 = &ctx->EvalMap.Map2Texture3;
	 break;
      case GL_MAP2_TEXTURE_COORD_4:
         map2 = &ctx->EvalMap.Map2Texture4;
	 break;
      default:
	 gl_error( ctx, GL_INVALID_ENUM, "gl_free_control_points" );
         return;
   }

   if (map1) {
      if (data==map1->Points) {
         /* The control points in the display list are currently */
         /* being used so we can mark them as discard-able. */
         map1->Retain = GL_FALSE;
      }
      else {
         /* The control points in the display list are not currently */
         /* being used. */
         free( data );
      }
   }
   if (map2) {
      if (data==map2->Points) {
         /* The control points in the display list are currently */
         /* being used so we can mark them as discard-able. */
         map2->Retain = GL_FALSE;
      }
      else {
         /* The control points in the display list are not currently */
         /* being used. */
         free( data );
      }
   }

}



/**********************************************************************/
/***                      API entry points                          ***/
/**********************************************************************/


/*
 * Note that the array of control points must be 'unpacked' at this time.
 * Input:  retain - if TRUE, this control point data is also in a display
 *                  list and can't be freed until the list is freed.
 */
void gl_Map1f( GLcontext* ctx, GLenum target,
               GLfloat u1, GLfloat u2, GLint stride,
               GLint order, const GLfloat *points, GLboolean retain )
{
   GLuint k;

   if (!points) {
      gl_error( ctx, GL_OUT_OF_MEMORY, "glMap1f" );
      return;
   }

   /* may be a new stride after copying control points */
   stride = components( target );

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

   if (u1==u2) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap1(u1,u2)" );
      return;
   }

   if (order<1 || order>MAX_EVAL_ORDER) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap1(order)" );
      return;
   }

   k = components( target );
   if (k==0) {
      gl_error( ctx, GL_INVALID_ENUM, "glMap1(target)" );
   }

   if (stride < k) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap1(stride)" );
      return;
   }

   switch (target) {
      case GL_MAP1_VERTEX_3:
         ctx->EvalMap.Map1Vertex3.Order = order;
	 ctx->EvalMap.Map1Vertex3.u1 = u1;
	 ctx->EvalMap.Map1Vertex3.u2 = u2;
	 if (ctx->EvalMap.Map1Vertex3.Points
             && !ctx->EvalMap.Map1Vertex3.Retain) {
	    free( ctx->EvalMap.Map1Vertex3.Points );
	 }
	 ctx->EvalMap.Map1Vertex3.Points = (GLfloat *) points;
         ctx->EvalMap.Map1Vertex3.Retain = retain;
	 break;
      case GL_MAP1_VERTEX_4:
         ctx->EvalMap.Map1Vertex4.Order = order;
	 ctx->EvalMap.Map1Vertex4.u1 = u1;
	 ctx->EvalMap.Map1Vertex4.u2 = u2;
	 if (ctx->EvalMap.Map1Vertex4.Points
             && !ctx->EvalMap.Map1Vertex4.Retain) {
	    free( ctx->EvalMap.Map1Vertex4.Points );
	 }
	 ctx->EvalMap.Map1Vertex4.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Vertex4.Retain = retain;
	 break;
      case GL_MAP1_INDEX:
         ctx->EvalMap.Map1Index.Order = order;
	 ctx->EvalMap.Map1Index.u1 = u1;
	 ctx->EvalMap.Map1Index.u2 = u2;
	 if (ctx->EvalMap.Map1Index.Points
             && !ctx->EvalMap.Map1Index.Retain) {
	    free( ctx->EvalMap.Map1Index.Points );
	 }
	 ctx->EvalMap.Map1Index.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Index.Retain = retain;
	 break;
      case GL_MAP1_COLOR_4:
         ctx->EvalMap.Map1Color4.Order = order;
	 ctx->EvalMap.Map1Color4.u1 = u1;
	 ctx->EvalMap.Map1Color4.u2 = u2;
	 if (ctx->EvalMap.Map1Color4.Points
             && !ctx->EvalMap.Map1Color4.Retain) {
	    free( ctx->EvalMap.Map1Color4.Points );
	 }
	 ctx->EvalMap.Map1Color4.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Color4.Retain = retain;
	 break;
      case GL_MAP1_NORMAL:
         ctx->EvalMap.Map1Normal.Order = order;
	 ctx->EvalMap.Map1Normal.u1 = u1;
	 ctx->EvalMap.Map1Normal.u2 = u2;
	 if (ctx->EvalMap.Map1Normal.Points
             && !ctx->EvalMap.Map1Normal.Retain) {
	    free( ctx->EvalMap.Map1Normal.Points );
	 }
	 ctx->EvalMap.Map1Normal.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Normal.Retain = retain;
	 break;
      case GL_MAP1_TEXTURE_COORD_1:
         ctx->EvalMap.Map1Texture1.Order = order;
	 ctx->EvalMap.Map1Texture1.u1 = u1;
	 ctx->EvalMap.Map1Texture1.u2 = u2;
	 if (ctx->EvalMap.Map1Texture1.Points
             && !ctx->EvalMap.Map1Texture1.Retain) {
	    free( ctx->EvalMap.Map1Texture1.Points );
	 }
	 ctx->EvalMap.Map1Texture1.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Texture1.Retain = retain;
	 break;
      case GL_MAP1_TEXTURE_COORD_2:
         ctx->EvalMap.Map1Texture2.Order = order;
	 ctx->EvalMap.Map1Texture2.u1 = u1;
	 ctx->EvalMap.Map1Texture2.u2 = u2;
	 if (ctx->EvalMap.Map1Texture2.Points
             && !ctx->EvalMap.Map1Texture2.Retain) {
	    free( ctx->EvalMap.Map1Texture2.Points );
	 }
	 ctx->EvalMap.Map1Texture2.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Texture2.Retain = retain;
	 break;
      case GL_MAP1_TEXTURE_COORD_3:
         ctx->EvalMap.Map1Texture3.Order = order;
	 ctx->EvalMap.Map1Texture3.u1 = u1;
	 ctx->EvalMap.Map1Texture3.u2 = u2;
	 if (ctx->EvalMap.Map1Texture3.Points
             && !ctx->EvalMap.Map1Texture3.Retain) {
	    free( ctx->EvalMap.Map1Texture3.Points );
	 }
	 ctx->EvalMap.Map1Texture3.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Texture3.Retain = retain;
	 break;
      case GL_MAP1_TEXTURE_COORD_4:
         ctx->EvalMap.Map1Texture4.Order = order;
	 ctx->EvalMap.Map1Texture4.u1 = u1;
	 ctx->EvalMap.Map1Texture4.u2 = u2;
	 if (ctx->EvalMap.Map1Texture4.Points
             && !ctx->EvalMap.Map1Texture4.Retain) {
	    free( ctx->EvalMap.Map1Texture4.Points );
	 }
	 ctx->EvalMap.Map1Texture4.Points = (GLfloat *) points;
	 ctx->EvalMap.Map1Texture4.Retain = retain;
	 break;
      default:
         gl_error( ctx, GL_INVALID_ENUM, "glMap1(target)" );
   }
}




/*
 * Note that the array of control points must be 'unpacked' at this time.
 * Input:  retain - if TRUE, this control point data is also in a display
 *                  list and can't be freed until the list is freed.
 */
void gl_Map2f( GLcontext* ctx, GLenum target,
	      GLfloat u1, GLfloat u2, GLint ustride, GLint uorder,
	      GLfloat v1, GLfloat v2, GLint vstride, GLint vorder,
	      const GLfloat *points, GLboolean retain )
{
   GLuint k;

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

   if (u1==u2) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap2(u1,u2)" );
      return;
   }

   if (v1==v2) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap2(v1,v2)" );
      return;
   }

   if (uorder<1 || uorder>MAX_EVAL_ORDER) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap2(uorder)" );
      return;
   }

   if (vorder<1 || vorder>MAX_EVAL_ORDER) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap2(vorder)" );
      return;
   }

   k = components( target );
   if (k==0) {
      gl_error( ctx, GL_INVALID_ENUM, "glMap2(target)" );
   }

   if (ustride < k) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap2(ustride)" );
      return;
   }
   if (vstride < k) {
      gl_error( ctx, GL_INVALID_VALUE, "glMap2(vstride)" );
      return;
   }

   switch (target) {
      case GL_MAP2_VERTEX_3:
         ctx->EvalMap.Map2Vertex3.Uorder = uorder;
	 ctx->EvalMap.Map2Vertex3.u1 = u1;
	 ctx->EvalMap.Map2Vertex3.u2 = u2;
         ctx->EvalMap.Map2Vertex3.Vorder = vorder;
	 ctx->EvalMap.Map2Vertex3.v1 = v1;
	 ctx->EvalMap.Map2Vertex3.v2 = v2;
	 if (ctx->EvalMap.Map2Vertex3.Points
             && !ctx->EvalMap.Map2Vertex3.Retain) {
	    free( ctx->EvalMap.Map2Vertex3.Points );
	 }
	 ctx->EvalMap.Map2Vertex3.Retain = retain;
	 ctx->EvalMap.Map2Vertex3.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_VERTEX_4:
         ctx->EvalMap.Map2Vertex4.Uorder = uorder;
	 ctx->EvalMap.Map2Vertex4.u1 = u1;
	 ctx->EvalMap.Map2Vertex4.u2 = u2;
         ctx->EvalMap.Map2Vertex4.Vorder = vorder;
	 ctx->EvalMap.Map2Vertex4.v1 = v1;
	 ctx->EvalMap.Map2Vertex4.v2 = v2;
	 if (ctx->EvalMap.Map2Vertex4.Points
             && !ctx->EvalMap.Map2Vertex4.Retain) {
	    free( ctx->EvalMap.Map2Vertex4.Points );
	 }
	 ctx->EvalMap.Map2Vertex4.Points = (GLfloat *) points;
	 ctx->EvalMap.Map2Vertex4.Retain = retain;
	 break;
      case GL_MAP2_INDEX:
         ctx->EvalMap.Map2Index.Uorder = uorder;
	 ctx->EvalMap.Map2Index.u1 = u1;
	 ctx->EvalMap.Map2Index.u2 = u2;
         ctx->EvalMap.Map2Index.Vorder = vorder;
	 ctx->EvalMap.Map2Index.v1 = v1;
	 ctx->EvalMap.Map2Index.v2 = v2;
	 if (ctx->EvalMap.Map2Index.Points
             && !ctx->EvalMap.Map2Index.Retain) {
	    free( ctx->EvalMap.Map2Index.Points );
	 }
	 ctx->EvalMap.Map2Index.Retain = retain;
	 ctx->EvalMap.Map2Index.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_COLOR_4:
         ctx->EvalMap.Map2Color4.Uorder = uorder;
	 ctx->EvalMap.Map2Color4.u1 = u1;
	 ctx->EvalMap.Map2Color4.u2 = u2;
         ctx->EvalMap.Map2Color4.Vorder = vorder;
	 ctx->EvalMap.Map2Color4.v1 = v1;
	 ctx->EvalMap.Map2Color4.v2 = v2;
	 if (ctx->EvalMap.Map2Color4.Points
             && !ctx->EvalMap.Map2Color4.Retain) {
	    free( ctx->EvalMap.Map2Color4.Points );
	 }
	 ctx->EvalMap.Map2Color4.Retain = retain;
	 ctx->EvalMap.Map2Color4.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_NORMAL:
         ctx->EvalMap.Map2Normal.Uorder = uorder;
	 ctx->EvalMap.Map2Normal.u1 = u1;
	 ctx->EvalMap.Map2Normal.u2 = u2;
         ctx->EvalMap.Map2Normal.Vorder = vorder;
	 ctx->EvalMap.Map2Normal.v1 = v1;
	 ctx->EvalMap.Map2Normal.v2 = v2;
	 if (ctx->EvalMap.Map2Normal.Points
             && !ctx->EvalMap.Map2Normal.Retain) {
	    free( ctx->EvalMap.Map2Normal.Points );
	 }
	 ctx->EvalMap.Map2Normal.Retain = retain;
	 ctx->EvalMap.Map2Normal.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_TEXTURE_COORD_1:
         ctx->EvalMap.Map2Texture1.Uorder = uorder;
	 ctx->EvalMap.Map2Texture1.u1 = u1;
	 ctx->EvalMap.Map2Texture1.u2 = u2;
         ctx->EvalMap.Map2Texture1.Vorder = vorder;
	 ctx->EvalMap.Map2Texture1.v1 = v1;
	 ctx->EvalMap.Map2Texture1.v2 = v2;
	 if (ctx->EvalMap.Map2Texture1.Points
             && !ctx->EvalMap.Map2Texture1.Retain) {
	    free( ctx->EvalMap.Map2Texture1.Points );
	 }
	 ctx->EvalMap.Map2Texture1.Retain = retain;
	 ctx->EvalMap.Map2Texture1.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_TEXTURE_COORD_2:
         ctx->EvalMap.Map2Texture2.Uorder = uorder;
	 ctx->EvalMap.Map2Texture2.u1 = u1;
	 ctx->EvalMap.Map2Texture2.u2 = u2;
         ctx->EvalMap.Map2Texture2.Vorder = vorder;
	 ctx->EvalMap.Map2Texture2.v1 = v1;
	 ctx->EvalMap.Map2Texture2.v2 = v2;
	 if (ctx->EvalMap.Map2Texture2.Points
             && !ctx->EvalMap.Map2Texture2.Retain) {
	    free( ctx->EvalMap.Map2Texture2.Points );
	 }
	 ctx->EvalMap.Map2Texture2.Retain = retain;
	 ctx->EvalMap.Map2Texture2.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_TEXTURE_COORD_3:
         ctx->EvalMap.Map2Texture3.Uorder = uorder;
	 ctx->EvalMap.Map2Texture3.u1 = u1;
	 ctx->EvalMap.Map2Texture3.u2 = u2;
         ctx->EvalMap.Map2Texture3.Vorder = vorder;
	 ctx->EvalMap.Map2Texture3.v1 = v1;
	 ctx->EvalMap.Map2Texture3.v2 = v2;
	 if (ctx->EvalMap.Map2Texture3.Points
             && !ctx->EvalMap.Map2Texture3.Retain) {
	    free( ctx->EvalMap.Map2Texture3.Points );
	 }
	 ctx->EvalMap.Map2Texture3.Retain = retain;
	 ctx->EvalMap.Map2Texture3.Points = (GLfloat *) points;
	 break;
      case GL_MAP2_TEXTURE_COORD_4:
         ctx->EvalMap.Map2Texture4.Uorder = uorder;
	 ctx->EvalMap.Map2Texture4.u1 = u1;
	 ctx->EvalMap.Map2Texture4.u2 = u2;
         ctx->EvalMap.Map2Texture4.Vorder = vorder;
	 ctx->EvalMap.Map2Texture4.v1 = v1;
	 ctx->EvalMap.Map2Texture4.v2 = v2;
	 if (ctx->EvalMap.Map2Texture4.Points
             && !ctx->EvalMap.Map2Texture4.Retain) {
	    free( ctx->EvalMap.Map2Texture4.Points );
	 }
	 ctx->EvalMap.Map2Texture4.Retain = retain;
	 ctx->EvalMap.Map2Texture4.Points = (GLfloat *) points;
	 break;
      default:
         gl_error( ctx, GL_INVALID_ENUM, "glMap2(target)" );
   }
}


   


void gl_GetMapdv( GLcontext* ctx, GLenum target, GLenum query, GLdouble *v )
{
   GLuint i, n;
   GLfloat *data;

   switch (query) {
      case GL_COEFF:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       data = ctx->EvalMap.Map1Color4.Points;
	       n = ctx->EvalMap.Map1Color4.Order * 4;
	       break;
	    case GL_MAP1_INDEX:
	       data = ctx->EvalMap.Map1Index.Points;
	       n = ctx->EvalMap.Map1Index.Order;
	       break;
	    case GL_MAP1_NORMAL:
	       data = ctx->EvalMap.Map1Normal.Points;
	       n = ctx->EvalMap.Map1Normal.Order * 3;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       data = ctx->EvalMap.Map1Texture1.Points;
	       n = ctx->EvalMap.Map1Texture1.Order * 1;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       data = ctx->EvalMap.Map1Texture2.Points;
	       n = ctx->EvalMap.Map1Texture2.Order * 2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       data = ctx->EvalMap.Map1Texture3.Points;
	       n = ctx->EvalMap.Map1Texture3.Order * 3;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       data = ctx->EvalMap.Map1Texture4.Points;
	       n = ctx->EvalMap.Map1Texture4.Order * 4;
	       break;
	    case GL_MAP1_VERTEX_3:
	       data = ctx->EvalMap.Map1Vertex3.Points;
	       n = ctx->EvalMap.Map1Vertex3.Order * 3;
	       break;
	    case GL_MAP1_VERTEX_4:
	       data = ctx->EvalMap.Map1Vertex4.Points;
	       n = ctx->EvalMap.Map1Vertex4.Order * 4;
	       break;
	    case GL_MAP2_COLOR_4:
	       data = ctx->EvalMap.Map2Color4.Points;
	       n = ctx->EvalMap.Map2Color4.Uorder
                 * ctx->EvalMap.Map2Color4.Vorder * 4;
	       break;
	    case GL_MAP2_INDEX:
	       data = ctx->EvalMap.Map2Index.Points;
	       n = ctx->EvalMap.Map2Index.Uorder
                 * ctx->EvalMap.Map2Index.Vorder;
	       break;
	    case GL_MAP2_NORMAL:
	       data = ctx->EvalMap.Map2Normal.Points;
	       n = ctx->EvalMap.Map2Normal.Uorder
                 * ctx->EvalMap.Map2Normal.Vorder * 3;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       data = ctx->EvalMap.Map2Texture1.Points;
	       n = ctx->EvalMap.Map2Texture1.Uorder
                 * ctx->EvalMap.Map2Texture1.Vorder * 1;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       data = ctx->EvalMap.Map2Texture2.Points;
	       n = ctx->EvalMap.Map2Texture2.Uorder
                 * ctx->EvalMap.Map2Texture2.Vorder * 2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       data = ctx->EvalMap.Map2Texture3.Points;
	       n = ctx->EvalMap.Map2Texture3.Uorder
                 * ctx->EvalMap.Map2Texture3.Vorder * 3;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       data = ctx->EvalMap.Map2Texture4.Points;
	       n = ctx->EvalMap.Map2Texture4.Uorder
                 * ctx->EvalMap.Map2Texture4.Vorder * 4;
	       break;
	    case GL_MAP2_VERTEX_3:
	       data = ctx->EvalMap.Map2Vertex3.Points;
	       n = ctx->EvalMap.Map2Vertex3.Uorder
                 * ctx->EvalMap.Map2Vertex3.Vorder * 3;
	       break;
	    case GL_MAP2_VERTEX_4:
	       data = ctx->EvalMap.Map2Vertex4.Points;
	       n = ctx->EvalMap.Map2Vertex4.Uorder
                 * ctx->EvalMap.Map2Vertex4.Vorder * 4;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapdv(target)" );
	 }
	 if (data) {
	    for (i=0;i<n;i++) {
	       v[i] = data[i];
	    }
	 }
         break;
      case GL_ORDER:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       *v = ctx->EvalMap.Map1Color4.Order;
	       break;
	    case GL_MAP1_INDEX:
	       *v = ctx->EvalMap.Map1Index.Order;
	       break;
	    case GL_MAP1_NORMAL:
	       *v = ctx->EvalMap.Map1Normal.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       *v = ctx->EvalMap.Map1Texture1.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       *v = ctx->EvalMap.Map1Texture2.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       *v = ctx->EvalMap.Map1Texture3.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       *v = ctx->EvalMap.Map1Texture4.Order;
	       break;
	    case GL_MAP1_VERTEX_3:
	       *v = ctx->EvalMap.Map1Vertex3.Order;
	       break;
	    case GL_MAP1_VERTEX_4:
	       *v = ctx->EvalMap.Map1Vertex4.Order;
	       break;
	    case GL_MAP2_COLOR_4:
	       v[0] = ctx->EvalMap.Map2Color4.Uorder;
	       v[1] = ctx->EvalMap.Map2Color4.Vorder;
	       break;
	    case GL_MAP2_INDEX:
	       v[0] = ctx->EvalMap.Map2Index.Uorder;
	       v[1] = ctx->EvalMap.Map2Index.Vorder;
	       break;
	    case GL_MAP2_NORMAL:
	       v[0] = ctx->EvalMap.Map2Normal.Uorder;
	       v[1] = ctx->EvalMap.Map2Normal.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map2Texture1.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture1.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map2Texture2.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture2.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map2Texture3.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture3.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map2Texture4.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture4.Vorder;
	       break;
	    case GL_MAP2_VERTEX_3:
	       v[0] = ctx->EvalMap.Map2Vertex3.Uorder;
	       v[1] = ctx->EvalMap.Map2Vertex3.Vorder;
	       break;
	    case GL_MAP2_VERTEX_4:
	       v[0] = ctx->EvalMap.Map2Vertex4.Uorder;
	       v[1] = ctx->EvalMap.Map2Vertex4.Vorder;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapdv(target)" );
	 }
         break;
      case GL_DOMAIN:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       v[0] = ctx->EvalMap.Map1Color4.u1;
	       v[1] = ctx->EvalMap.Map1Color4.u2;
	       break;
	    case GL_MAP1_INDEX:
	       v[0] = ctx->EvalMap.Map1Index.u1;
	       v[1] = ctx->EvalMap.Map1Index.u2;
	       break;
	    case GL_MAP1_NORMAL:
	       v[0] = ctx->EvalMap.Map1Normal.u1;
	       v[1] = ctx->EvalMap.Map1Normal.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map1Texture1.u1;
	       v[1] = ctx->EvalMap.Map1Texture1.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map1Texture2.u1;
	       v[1] = ctx->EvalMap.Map1Texture2.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map1Texture3.u1;
	       v[1] = ctx->EvalMap.Map1Texture3.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map1Texture4.u1;
	       v[1] = ctx->EvalMap.Map1Texture4.u2;
	       break;
	    case GL_MAP1_VERTEX_3:
	       v[0] = ctx->EvalMap.Map1Vertex3.u1;
	       v[1] = ctx->EvalMap.Map1Vertex3.u2;
	       break;
	    case GL_MAP1_VERTEX_4:
	       v[0] = ctx->EvalMap.Map1Vertex4.u1;
	       v[1] = ctx->EvalMap.Map1Vertex4.u2;
	       break;
	    case GL_MAP2_COLOR_4:
	       v[0] = ctx->EvalMap.Map2Color4.u1;
	       v[1] = ctx->EvalMap.Map2Color4.u2;
	       v[2] = ctx->EvalMap.Map2Color4.v1;
	       v[3] = ctx->EvalMap.Map2Color4.v2;
	       break;
	    case GL_MAP2_INDEX:
	       v[0] = ctx->EvalMap.Map2Index.u1;
	       v[1] = ctx->EvalMap.Map2Index.u2;
	       v[2] = ctx->EvalMap.Map2Index.v1;
	       v[3] = ctx->EvalMap.Map2Index.v2;
	       break;
	    case GL_MAP2_NORMAL:
	       v[0] = ctx->EvalMap.Map2Normal.u1;
	       v[1] = ctx->EvalMap.Map2Normal.u2;
	       v[2] = ctx->EvalMap.Map2Normal.v1;
	       v[3] = ctx->EvalMap.Map2Normal.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map2Texture1.u1;
	       v[1] = ctx->EvalMap.Map2Texture1.u2;
	       v[2] = ctx->EvalMap.Map2Texture1.v1;
	       v[3] = ctx->EvalMap.Map2Texture1.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map2Texture2.u1;
	       v[1] = ctx->EvalMap.Map2Texture2.u2;
	       v[2] = ctx->EvalMap.Map2Texture2.v1;
	       v[3] = ctx->EvalMap.Map2Texture2.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map2Texture3.u1;
	       v[1] = ctx->EvalMap.Map2Texture3.u2;
	       v[2] = ctx->EvalMap.Map2Texture3.v1;
	       v[3] = ctx->EvalMap.Map2Texture3.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map2Texture4.u1;
	       v[1] = ctx->EvalMap.Map2Texture4.u2;
	       v[2] = ctx->EvalMap.Map2Texture4.v1;
	       v[3] = ctx->EvalMap.Map2Texture4.v2;
	       break;
	    case GL_MAP2_VERTEX_3:
	       v[0] = ctx->EvalMap.Map2Vertex3.u1;
	       v[1] = ctx->EvalMap.Map2Vertex3.u2;
	       v[2] = ctx->EvalMap.Map2Vertex3.v1;
	       v[3] = ctx->EvalMap.Map2Vertex3.v2;
	       break;
	    case GL_MAP2_VERTEX_4:
	       v[0] = ctx->EvalMap.Map2Vertex4.u1;
	       v[1] = ctx->EvalMap.Map2Vertex4.u2;
	       v[2] = ctx->EvalMap.Map2Vertex4.v1;
	       v[3] = ctx->EvalMap.Map2Vertex4.v2;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapdv(target)" );
	 }
         break;
      default:
         gl_error( ctx, GL_INVALID_ENUM, "glGetMapdv(query)" );
   }
}


void gl_GetMapfv( GLcontext* ctx, GLenum target, GLenum query, GLfloat *v )
{
   GLuint i, n;
   GLfloat *data;

   switch (query) {
      case GL_COEFF:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       data = ctx->EvalMap.Map1Color4.Points;
	       n = ctx->EvalMap.Map1Color4.Order * 4;
	       break;
	    case GL_MAP1_INDEX:
	       data = ctx->EvalMap.Map1Index.Points;
	       n = ctx->EvalMap.Map1Index.Order;
	       break;
	    case GL_MAP1_NORMAL:
	       data = ctx->EvalMap.Map1Normal.Points;
	       n = ctx->EvalMap.Map1Normal.Order * 3;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       data = ctx->EvalMap.Map1Texture1.Points;
	       n = ctx->EvalMap.Map1Texture1.Order * 1;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       data = ctx->EvalMap.Map1Texture2.Points;
	       n = ctx->EvalMap.Map1Texture2.Order * 2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       data = ctx->EvalMap.Map1Texture3.Points;
	       n = ctx->EvalMap.Map1Texture3.Order * 3;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       data = ctx->EvalMap.Map1Texture4.Points;
	       n = ctx->EvalMap.Map1Texture4.Order * 4;
	       break;
	    case GL_MAP1_VERTEX_3:
	       data = ctx->EvalMap.Map1Vertex3.Points;
	       n = ctx->EvalMap.Map1Vertex3.Order * 3;
	       break;
	    case GL_MAP1_VERTEX_4:
	       data = ctx->EvalMap.Map1Vertex4.Points;
	       n = ctx->EvalMap.Map1Vertex4.Order * 4;
	       break;
	    case GL_MAP2_COLOR_4:
	       data = ctx->EvalMap.Map2Color4.Points;
	       n = ctx->EvalMap.Map2Color4.Uorder
                 * ctx->EvalMap.Map2Color4.Vorder * 4;
	       break;
	    case GL_MAP2_INDEX:
	       data = ctx->EvalMap.Map2Index.Points;
	       n = ctx->EvalMap.Map2Index.Uorder
                 * ctx->EvalMap.Map2Index.Vorder;
	       break;
	    case GL_MAP2_NORMAL:
	       data = ctx->EvalMap.Map2Normal.Points;
	       n = ctx->EvalMap.Map2Normal.Uorder
                 * ctx->EvalMap.Map2Normal.Vorder * 3;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       data = ctx->EvalMap.Map2Texture1.Points;
	       n = ctx->EvalMap.Map2Texture1.Uorder
                 * ctx->EvalMap.Map2Texture1.Vorder * 1;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       data = ctx->EvalMap.Map2Texture2.Points;
	       n = ctx->EvalMap.Map2Texture2.Uorder
                 * ctx->EvalMap.Map2Texture2.Vorder * 2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       data = ctx->EvalMap.Map2Texture3.Points;
	       n = ctx->EvalMap.Map2Texture3.Uorder
                 * ctx->EvalMap.Map2Texture3.Vorder * 3;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       data = ctx->EvalMap.Map2Texture4.Points;
	       n = ctx->EvalMap.Map2Texture4.Uorder
                 * ctx->EvalMap.Map2Texture4.Vorder * 4;
	       break;
	    case GL_MAP2_VERTEX_3:
	       data = ctx->EvalMap.Map2Vertex3.Points;
	       n = ctx->EvalMap.Map2Vertex3.Uorder
                 * ctx->EvalMap.Map2Vertex3.Vorder * 3;
	       break;
	    case GL_MAP2_VERTEX_4:
	       data = ctx->EvalMap.Map2Vertex4.Points;
	       n = ctx->EvalMap.Map2Vertex4.Uorder
                 * ctx->EvalMap.Map2Vertex4.Vorder * 4;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapfv(target)" );
	 }
	 if (data) {
	    for (i=0;i<n;i++) {
	       v[i] = data[i];
	    }
	 }
         break;
      case GL_ORDER:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       *v = ctx->EvalMap.Map1Color4.Order;
	       break;
	    case GL_MAP1_INDEX:
	       *v = ctx->EvalMap.Map1Index.Order;
	       break;
	    case GL_MAP1_NORMAL:
	       *v = ctx->EvalMap.Map1Normal.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       *v = ctx->EvalMap.Map1Texture1.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       *v = ctx->EvalMap.Map1Texture2.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       *v = ctx->EvalMap.Map1Texture3.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       *v = ctx->EvalMap.Map1Texture4.Order;
	       break;
	    case GL_MAP1_VERTEX_3:
	       *v = ctx->EvalMap.Map1Vertex3.Order;
	       break;
	    case GL_MAP1_VERTEX_4:
	       *v = ctx->EvalMap.Map1Vertex4.Order;
	       break;
	    case GL_MAP2_COLOR_4:
	       v[0] = ctx->EvalMap.Map2Color4.Uorder;
	       v[1] = ctx->EvalMap.Map2Color4.Vorder;
	       break;
	    case GL_MAP2_INDEX:
	       v[0] = ctx->EvalMap.Map2Index.Uorder;
	       v[1] = ctx->EvalMap.Map2Index.Vorder;
	       break;
	    case GL_MAP2_NORMAL:
	       v[0] = ctx->EvalMap.Map2Normal.Uorder;
	       v[1] = ctx->EvalMap.Map2Normal.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map2Texture1.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture1.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map2Texture2.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture2.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map2Texture3.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture3.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map2Texture4.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture4.Vorder;
	       break;
	    case GL_MAP2_VERTEX_3:
	       v[0] = ctx->EvalMap.Map2Vertex3.Uorder;
	       v[1] = ctx->EvalMap.Map2Vertex3.Vorder;
	       break;
	    case GL_MAP2_VERTEX_4:
	       v[0] = ctx->EvalMap.Map2Vertex4.Uorder;
	       v[1] = ctx->EvalMap.Map2Vertex4.Vorder;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapfv(target)" );
	 }
         break;
      case GL_DOMAIN:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       v[0] = ctx->EvalMap.Map1Color4.u1;
	       v[1] = ctx->EvalMap.Map1Color4.u2;
	       break;
	    case GL_MAP1_INDEX:
	       v[0] = ctx->EvalMap.Map1Index.u1;
	       v[1] = ctx->EvalMap.Map1Index.u2;
	       break;
	    case GL_MAP1_NORMAL:
	       v[0] = ctx->EvalMap.Map1Normal.u1;
	       v[1] = ctx->EvalMap.Map1Normal.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map1Texture1.u1;
	       v[1] = ctx->EvalMap.Map1Texture1.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map1Texture2.u1;
	       v[1] = ctx->EvalMap.Map1Texture2.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map1Texture3.u1;
	       v[1] = ctx->EvalMap.Map1Texture3.u2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map1Texture4.u1;
	       v[1] = ctx->EvalMap.Map1Texture4.u2;
	       break;
	    case GL_MAP1_VERTEX_3:
	       v[0] = ctx->EvalMap.Map1Vertex3.u1;
	       v[1] = ctx->EvalMap.Map1Vertex3.u2;
	       break;
	    case GL_MAP1_VERTEX_4:
	       v[0] = ctx->EvalMap.Map1Vertex4.u1;
	       v[1] = ctx->EvalMap.Map1Vertex4.u2;
	       break;
	    case GL_MAP2_COLOR_4:
	       v[0] = ctx->EvalMap.Map2Color4.u1;
	       v[1] = ctx->EvalMap.Map2Color4.u2;
	       v[2] = ctx->EvalMap.Map2Color4.v1;
	       v[3] = ctx->EvalMap.Map2Color4.v2;
	       break;
	    case GL_MAP2_INDEX:
	       v[0] = ctx->EvalMap.Map2Index.u1;
	       v[1] = ctx->EvalMap.Map2Index.u2;
	       v[2] = ctx->EvalMap.Map2Index.v1;
	       v[3] = ctx->EvalMap.Map2Index.v2;
	       break;
	    case GL_MAP2_NORMAL:
	       v[0] = ctx->EvalMap.Map2Normal.u1;
	       v[1] = ctx->EvalMap.Map2Normal.u2;
	       v[2] = ctx->EvalMap.Map2Normal.v1;
	       v[3] = ctx->EvalMap.Map2Normal.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map2Texture1.u1;
	       v[1] = ctx->EvalMap.Map2Texture1.u2;
	       v[2] = ctx->EvalMap.Map2Texture1.v1;
	       v[3] = ctx->EvalMap.Map2Texture1.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map2Texture2.u1;
	       v[1] = ctx->EvalMap.Map2Texture2.u2;
	       v[2] = ctx->EvalMap.Map2Texture2.v1;
	       v[3] = ctx->EvalMap.Map2Texture2.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map2Texture3.u1;
	       v[1] = ctx->EvalMap.Map2Texture3.u2;
	       v[2] = ctx->EvalMap.Map2Texture3.v1;
	       v[3] = ctx->EvalMap.Map2Texture3.v2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map2Texture4.u1;
	       v[1] = ctx->EvalMap.Map2Texture4.u2;
	       v[2] = ctx->EvalMap.Map2Texture4.v1;
	       v[3] = ctx->EvalMap.Map2Texture4.v2;
	       break;
	    case GL_MAP2_VERTEX_3:
	       v[0] = ctx->EvalMap.Map2Vertex3.u1;
	       v[1] = ctx->EvalMap.Map2Vertex3.u2;
	       v[2] = ctx->EvalMap.Map2Vertex3.v1;
	       v[3] = ctx->EvalMap.Map2Vertex3.v2;
	       break;
	    case GL_MAP2_VERTEX_4:
	       v[0] = ctx->EvalMap.Map2Vertex4.u1;
	       v[1] = ctx->EvalMap.Map2Vertex4.u2;
	       v[2] = ctx->EvalMap.Map2Vertex4.v1;
	       v[3] = ctx->EvalMap.Map2Vertex4.v2;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapfv(target)" );
	 }
         break;
      default:
         gl_error( ctx, GL_INVALID_ENUM, "glGetMapfv(query)" );
   }
}


void gl_GetMapiv( GLcontext* ctx, GLenum target, GLenum query, GLint *v )
{
   GLuint i, n;
   GLfloat *data;

   switch (query) {
      case GL_COEFF:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       data = ctx->EvalMap.Map1Color4.Points;
	       n = ctx->EvalMap.Map1Color4.Order * 4;
	       break;
	    case GL_MAP1_INDEX:
	       data = ctx->EvalMap.Map1Index.Points;
	       n = ctx->EvalMap.Map1Index.Order;
	       break;
	    case GL_MAP1_NORMAL:
	       data = ctx->EvalMap.Map1Normal.Points;
	       n = ctx->EvalMap.Map1Normal.Order * 3;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       data = ctx->EvalMap.Map1Texture1.Points;
	       n = ctx->EvalMap.Map1Texture1.Order * 1;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       data = ctx->EvalMap.Map1Texture2.Points;
	       n = ctx->EvalMap.Map1Texture2.Order * 2;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       data = ctx->EvalMap.Map1Texture3.Points;
	       n = ctx->EvalMap.Map1Texture3.Order * 3;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       data = ctx->EvalMap.Map1Texture4.Points;
	       n = ctx->EvalMap.Map1Texture4.Order * 4;
	       break;
	    case GL_MAP1_VERTEX_3:
	       data = ctx->EvalMap.Map1Vertex3.Points;
	       n = ctx->EvalMap.Map1Vertex3.Order * 3;
	       break;
	    case GL_MAP1_VERTEX_4:
	       data = ctx->EvalMap.Map1Vertex4.Points;
	       n = ctx->EvalMap.Map1Vertex4.Order * 4;
	       break;
	    case GL_MAP2_COLOR_4:
	       data = ctx->EvalMap.Map2Color4.Points;
	       n = ctx->EvalMap.Map2Color4.Uorder
                 * ctx->EvalMap.Map2Color4.Vorder * 4;
	       break;
	    case GL_MAP2_INDEX:
	       data = ctx->EvalMap.Map2Index.Points;
	       n = ctx->EvalMap.Map2Index.Uorder
                 * ctx->EvalMap.Map2Index.Vorder;
	       break;
	    case GL_MAP2_NORMAL:
	       data = ctx->EvalMap.Map2Normal.Points;
	       n = ctx->EvalMap.Map2Normal.Uorder
                 * ctx->EvalMap.Map2Normal.Vorder * 3;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       data = ctx->EvalMap.Map2Texture1.Points;
	       n = ctx->EvalMap.Map2Texture1.Uorder
                 * ctx->EvalMap.Map2Texture1.Vorder * 1;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       data = ctx->EvalMap.Map2Texture2.Points;
	       n = ctx->EvalMap.Map2Texture2.Uorder
                 * ctx->EvalMap.Map2Texture2.Vorder * 2;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       data = ctx->EvalMap.Map2Texture3.Points;
	       n = ctx->EvalMap.Map2Texture3.Uorder
                 * ctx->EvalMap.Map2Texture3.Vorder * 3;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       data = ctx->EvalMap.Map2Texture4.Points;
	       n = ctx->EvalMap.Map2Texture4.Uorder
                 * ctx->EvalMap.Map2Texture4.Vorder * 4;
	       break;
	    case GL_MAP2_VERTEX_3:
	       data = ctx->EvalMap.Map2Vertex3.Points;
	       n = ctx->EvalMap.Map2Vertex3.Uorder
                 * ctx->EvalMap.Map2Vertex3.Vorder * 3;
	       break;
	    case GL_MAP2_VERTEX_4:
	       data = ctx->EvalMap.Map2Vertex4.Points;
	       n = ctx->EvalMap.Map2Vertex4.Uorder
                 * ctx->EvalMap.Map2Vertex4.Vorder * 4;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapiv(target)" );
	 }
	 if (data) {
	    for (i=0;i<n;i++) {
	       v[i] = ROUNDF(data[i]);
	    }
	 }
         break;
      case GL_ORDER:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       *v = ctx->EvalMap.Map1Color4.Order;
	       break;
	    case GL_MAP1_INDEX:
	       *v = ctx->EvalMap.Map1Index.Order;
	       break;
	    case GL_MAP1_NORMAL:
	       *v = ctx->EvalMap.Map1Normal.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       *v = ctx->EvalMap.Map1Texture1.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       *v = ctx->EvalMap.Map1Texture2.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       *v = ctx->EvalMap.Map1Texture3.Order;
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       *v = ctx->EvalMap.Map1Texture4.Order;
	       break;
	    case GL_MAP1_VERTEX_3:
	       *v = ctx->EvalMap.Map1Vertex3.Order;
	       break;
	    case GL_MAP1_VERTEX_4:
	       *v = ctx->EvalMap.Map1Vertex4.Order;
	       break;
	    case GL_MAP2_COLOR_4:
	       v[0] = ctx->EvalMap.Map2Color4.Uorder;
	       v[1] = ctx->EvalMap.Map2Color4.Vorder;
	       break;
	    case GL_MAP2_INDEX:
	       v[0] = ctx->EvalMap.Map2Index.Uorder;
	       v[1] = ctx->EvalMap.Map2Index.Vorder;
	       break;
	    case GL_MAP2_NORMAL:
	       v[0] = ctx->EvalMap.Map2Normal.Uorder;
	       v[1] = ctx->EvalMap.Map2Normal.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       v[0] = ctx->EvalMap.Map2Texture1.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture1.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       v[0] = ctx->EvalMap.Map2Texture2.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture2.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       v[0] = ctx->EvalMap.Map2Texture3.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture3.Vorder;
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       v[0] = ctx->EvalMap.Map2Texture4.Uorder;
	       v[1] = ctx->EvalMap.Map2Texture4.Vorder;
	       break;
	    case GL_MAP2_VERTEX_3:
	       v[0] = ctx->EvalMap.Map2Vertex3.Uorder;
	       v[1] = ctx->EvalMap.Map2Vertex3.Vorder;
	       break;
	    case GL_MAP2_VERTEX_4:
	       v[0] = ctx->EvalMap.Map2Vertex4.Uorder;
	       v[1] = ctx->EvalMap.Map2Vertex4.Vorder;
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapiv(target)" );
	 }
         break;
      case GL_DOMAIN:
	 switch (target) {
	    case GL_MAP1_COLOR_4:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Color4.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Color4.u2);
	       break;
	    case GL_MAP1_INDEX:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Index.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Index.u2);
	       break;
	    case GL_MAP1_NORMAL:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Normal.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Normal.u2);
	       break;
	    case GL_MAP1_TEXTURE_COORD_1:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Texture1.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Texture1.u2);
	       break;
	    case GL_MAP1_TEXTURE_COORD_2:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Texture2.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Texture2.u2);
	       break;
	    case GL_MAP1_TEXTURE_COORD_3:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Texture3.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Texture3.u2);
	       break;
	    case GL_MAP1_TEXTURE_COORD_4:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Texture4.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Texture4.u2);
	       break;
	    case GL_MAP1_VERTEX_3:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Vertex3.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Vertex3.u2);
	       break;
	    case GL_MAP1_VERTEX_4:
	       v[0] = ROUNDF(ctx->EvalMap.Map1Vertex4.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map1Vertex4.u2);
	       break;
	    case GL_MAP2_COLOR_4:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Color4.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Color4.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Color4.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Color4.v2);
	       break;
	    case GL_MAP2_INDEX:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Index.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Index.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Index.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Index.v2);
	       break;
	    case GL_MAP2_NORMAL:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Normal.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Normal.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Normal.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Normal.v2);
	       break;
	    case GL_MAP2_TEXTURE_COORD_1:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Texture1.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Texture1.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Texture1.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Texture1.v2);
	       break;
	    case GL_MAP2_TEXTURE_COORD_2:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Texture2.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Texture2.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Texture2.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Texture2.v2);
	       break;
	    case GL_MAP2_TEXTURE_COORD_3:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Texture3.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Texture3.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Texture3.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Texture3.v2);
	       break;
	    case GL_MAP2_TEXTURE_COORD_4:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Texture4.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Texture4.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Texture4.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Texture4.v2);
	       break;
	    case GL_MAP2_VERTEX_3:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Vertex3.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Vertex3.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Vertex3.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Vertex3.v2);
	       break;
	    case GL_MAP2_VERTEX_4:
	       v[0] = ROUNDF(ctx->EvalMap.Map2Vertex4.u1);
	       v[1] = ROUNDF(ctx->EvalMap.Map2Vertex4.u2);
	       v[2] = ROUNDF(ctx->EvalMap.Map2Vertex4.v1);
	       v[3] = ROUNDF(ctx->EvalMap.Map2Vertex4.v2);
	       break;
	    default:
	       gl_error( ctx, GL_INVALID_ENUM, "glGetMapiv(target)" );
	 }
         break;
      default:
         gl_error( ctx, GL_INVALID_ENUM, "glGetMapiv(query)" );
   }
}



void gl_EvalCoord1f(GLcontext* ctx, GLfloat u)
{
  GLfloat vertex[4];
  GLfloat normal[3];
  GLfloat fcolor[4];
  GLint icolor[4];
  GLint *colorptr;
  GLfloat texcoord[4];
  GLuint index;
  register GLfloat uu;

  /** Vertex **/
  if (ctx->Eval.Map1Vertex4) 
  {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Vertex4;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, vertex, uu, 4, map->Order);
  }
  else if (ctx->Eval.Map1Vertex3) 
  {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Vertex3;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, vertex, uu, 3, map->Order);
     vertex[3] = 1.0;
  }

  /** Color Index **/
  if (ctx->Eval.Map1Index) 
  {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Index;
     GLfloat findex;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, &findex, uu, 1, map->Order);
     index = (GLuint) (GLint) findex;
  }
  else {
     index = ctx->Current.Index;
  }

  /** Color **/
  if (ctx->Eval.Map1Color4) {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Color4;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, fcolor, uu, 4, map->Order);
     icolor[0] = (GLint) (fcolor[0] * ctx->Visual->RedScale);
     icolor[1] = (GLint) (fcolor[1] * ctx->Visual->GreenScale);
     icolor[2] = (GLint) (fcolor[2] * ctx->Visual->BlueScale);
     icolor[3] = (GLint) (fcolor[3] * ctx->Visual->AlphaScale);
     colorptr = icolor;
  }
  else {
     colorptr = ctx->Current.IntColor;
  }

  /** Normal Vector **/
  if (ctx->Eval.Map1Normal) {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Normal;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, normal, uu, 3, map->Order);
  }
  else {
    normal[0] = ctx->Current.Normal[0];
    normal[1] = ctx->Current.Normal[1];
    normal[2] = ctx->Current.Normal[2];
  }

  /** Texture Coordinates **/
  if (ctx->Eval.Map1TextureCoord4) {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Texture4;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, texcoord, uu, 4, map->Order);
  }
  else if (ctx->Eval.Map1TextureCoord3) {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Texture3;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, texcoord, uu, 3, map->Order);
     texcoord[3] = 1.0;
  }
  else if (ctx->Eval.Map1TextureCoord2) {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Texture2;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, texcoord, uu, 2, map->Order);
     texcoord[2] = 0.0;
     texcoord[3] = 1.0;
  }
  else if (ctx->Eval.Map1TextureCoord1) {
     struct gl_1d_map *map = &ctx->EvalMap.Map1Texture1;
     uu = (u - map->u1) / (map->u2 - map->u1);
     horner_bezier_curve(map->Points, texcoord, uu, 1, map->Order);
     texcoord[1] = 0.0;
     texcoord[2] = 0.0;
     texcoord[3] = 1.0;
  }
  else {
     texcoord[0] = ctx->Current.TexCoord[0];
     texcoord[1] = ctx->Current.TexCoord[1];
     texcoord[2] = ctx->Current.TexCoord[2];
     texcoord[3] = ctx->Current.TexCoord[3];
  }
  
  gl_eval_vertex( ctx, vertex, normal, colorptr, index, texcoord );
}


void gl_EvalCoord2f( GLcontext* ctx, GLfloat u, GLfloat v )
{
   GLfloat vertex[4];
   GLfloat normal[3];
   GLfloat fcolor[4];
   GLint icolor[4];
   GLint *colorptr;
   GLfloat texcoord[4];
   GLuint index;
   register GLfloat uu, vv;

#define CROSS_PROD(n, u, v) \
  (n)[0] = (u)[1]*(v)[2] - (u)[2]*(v)[1]; \
  (n)[1] = (u)[2]*(v)[0] - (u)[0]*(v)[2]; \
  (n)[2] = (u)[0]*(v)[1] - (u)[1]*(v)[0]
#define NORMALIZE(n) \
  { GLfloat l = sqrt((n)[0]*(n)[0] + (n)[1]*n[1] + (n)[2]*(n)[2]); \
    if(l > 0.000001) { (n)[0]/=l; (n)[1]/=l; (n)[2]/=l; } }

   /** Vertex **/
   if(ctx->Eval.Map2Vertex4) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Vertex4;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);

      if (ctx->Eval.AutoNormal) {
         GLfloat du[4], dv[4];

         de_casteljau_surf(map->Points, vertex, du, dv, uu, vv, 4,
                           map->Uorder, map->Vorder);

         CROSS_PROD(normal, du, dv);
         NORMALIZE(normal);
      }
      else {
         horner_bezier_surf(map->Points, vertex, uu, vv, 4,
                            map->Uorder, map->Vorder);
      }
   }
   else if (ctx->Eval.Map2Vertex3) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Vertex3;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      if (ctx->Eval.AutoNormal) {
         GLfloat du[3], dv[3];
         de_casteljau_surf(map->Points, vertex, du, dv, uu, vv, 3,
                           map->Uorder, map->Vorder);
         CROSS_PROD(normal, du, dv);
         NORMALIZE(normal);
      }
      else {
         horner_bezier_surf(map->Points, vertex, uu, vv, 3,
                            map->Uorder, map->Vorder);
      }
      vertex[3] = 1.0;
   }
#undef NORMALIZE
#undef CROSS_PROD
   
   /** Color Index **/
   if (ctx->Eval.Map2Index) {
      GLfloat findex;
      struct gl_2d_map *map = &ctx->EvalMap.Map2Index;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      horner_bezier_surf(map->Points, &findex, uu, vv, 1,
                         map->Uorder, map->Vorder);
      index = (GLuint) (GLint) findex;
   }
   else {
      index = ctx->Current.Index;
   }

   /** Color **/
   if (ctx->Eval.Map2Color4) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Color4;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      horner_bezier_surf(map->Points, fcolor, uu, vv, 4,
                         map->Uorder, map->Vorder);
      icolor[0] = (GLint) (fcolor[0] * ctx->Visual->RedScale);
      icolor[1] = (GLint) (fcolor[1] * ctx->Visual->GreenScale);
      icolor[2] = (GLint) (fcolor[2] * ctx->Visual->BlueScale);
      icolor[3] = (GLint) (fcolor[3] * ctx->Visual->AlphaScale);
      colorptr = icolor;
   }
   else {
      colorptr = ctx->Current.IntColor;
   }

   /** Normal **/
   if (!ctx->Eval.AutoNormal
       || (!ctx->Eval.Map2Vertex3 && !ctx->Eval.Map2Vertex4)) {
      if (ctx->Eval.Map2Normal) {
         struct gl_2d_map *map = &ctx->EvalMap.Map2Normal;
         uu = (u - map->u1) / (map->u2 - map->u1);
         vv = (v - map->v1) / (map->v2 - map->v1);
         horner_bezier_surf(map->Points, normal, uu, vv, 3,
                            map->Uorder, map->Vorder);
      }
      else {
         normal[0] = ctx->Current.Normal[0];
         normal[1] = ctx->Current.Normal[1];
         normal[2] = ctx->Current.Normal[2];
      }
   }

   /** Texture Coordinates **/
   if (ctx->Eval.Map2TextureCoord4) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Texture4;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      horner_bezier_surf(map->Points, texcoord, uu, vv, 4,
                         map->Uorder, map->Vorder);
   }
   else if (ctx->Eval.Map2TextureCoord3) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Texture3;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      horner_bezier_surf(map->Points, texcoord, uu, vv, 3,
                         map->Uorder, map->Vorder);
     texcoord[3] = 1.0;
   }
   else if (ctx->Eval.Map2TextureCoord2) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Texture2;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      horner_bezier_surf(map->Points, texcoord, uu, vv, 2,
                         map->Uorder, map->Vorder);
     texcoord[2] = 0.0;
     texcoord[3] = 1.0;
   }
   else if (ctx->Eval.Map2TextureCoord1) {
      struct gl_2d_map *map = &ctx->EvalMap.Map2Texture1;
      uu = (u - map->u1) / (map->u2 - map->u1);
      vv = (v - map->v1) / (map->v2 - map->v1);
      horner_bezier_surf(map->Points, texcoord, uu, vv, 1,
                         map->Uorder, map->Vorder);
     texcoord[1] = 0.0;
     texcoord[2] = 0.0;
     texcoord[3] = 1.0;
   }
   else 
   {
     texcoord[0] = ctx->Current.TexCoord[0];
     texcoord[1] = ctx->Current.TexCoord[1];
     texcoord[2] = ctx->Current.TexCoord[2];
     texcoord[3] = ctx->Current.TexCoord[3];
   }

   gl_eval_vertex( ctx, vertex, normal, colorptr, index, texcoord );
}


void gl_MapGrid1f( GLcontext* ctx, GLint un, GLfloat u1, GLfloat u2 )
{
   if (INSIDE_BEGIN_END(ctx)) {
      gl_error( ctx, GL_INVALID_OPERATION, "glMapGrid1f" );
      return;
   }
   if (un<1) {
      gl_error( ctx, GL_INVALID_VALUE, "glMapGrid1f" );
      return;
   }
   ctx->Eval.MapGrid1un = un;
   ctx->Eval.MapGrid1u1 = u1;
   ctx->Eval.MapGrid1u2 = u2;
}


void gl_MapGrid2f( GLcontext* ctx, GLint un, GLfloat u1, GLfloat u2,
		  GLint vn, GLfloat v1, GLfloat v2 )
{
   if (INSIDE_BEGIN_END(ctx)) {
      gl_error( ctx, GL_INVALID_OPERATION, "glMapGrid2f" );
      return;
   }
   if (un<1) {
      gl_error( ctx, GL_INVALID_VALUE, "glMapGrid2f(un)" );
      return;
   }
   if (vn<1) {
      gl_error( ctx, GL_INVALID_VALUE, "glMapGrid2f(vn)" );
      return;
   }
   ctx->Eval.MapGrid2un = un;
   ctx->Eval.MapGrid2u1 = u1;
   ctx->Eval.MapGrid2u2 = u2;
   ctx->Eval.MapGrid2vn = vn;
   ctx->Eval.MapGrid2v1 = v1;
   ctx->Eval.MapGrid2v2 = v2;
}


void gl_EvalPoint1( GLcontext* ctx, GLint i )
{
	GLfloat u, du;

	if (i==0) {
		u = ctx->Eval.MapGrid1u1;
	}
	else if (i==ctx->Eval.MapGrid1un) {
		u = ctx->Eval.MapGrid1u2;
	}
	else {
		du = (ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1)
			/ (GLfloat) ctx->Eval.MapGrid1un;
		u = i * du + ctx->Eval.MapGrid1u1;
	}
	gl_EvalCoord1f( ctx, u );
}



void gl_EvalPoint2( GLcontext* ctx, GLint i, GLint j )
{
	GLfloat u, du;
	GLfloat v, dv;

	if (i==0) {
		u = ctx->Eval.MapGrid2u1;
	}
	else if (i==ctx->Eval.MapGrid2un) {
		u = ctx->Eval.MapGrid2u2;
	}
	else {
		du = (ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1)
			/ (GLfloat) ctx->Eval.MapGrid2un;
		u = i * du + ctx->Eval.MapGrid2u1;
	}

	if (j==0) {
		v = ctx->Eval.MapGrid2v1;
	}
	else if (j==ctx->Eval.MapGrid2vn) {
		v = ctx->Eval.MapGrid2v2;
	}
	else {
		dv = (ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1)
			/ (GLfloat) ctx->Eval.MapGrid2vn;
		v = j * dv + ctx->Eval.MapGrid2v1;
	}

	gl_EvalCoord2f( ctx, u, v );
}



void gl_EvalMesh1( GLcontext* ctx, GLenum mode, GLint i1, GLint i2 )
{
   GLint i;
   GLfloat u, du;
   GLenum prim;

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

	switch (mode) {
	case GL_POINT:
		prim = GL_POINTS;
		break;
	case GL_LINE:
		prim = GL_LINE_STRIP;
		break;
	default:
		gl_error( ctx, GL_INVALID_ENUM, "glEvalMesh1(mode)" );
		return;
	}

	du = (ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1)
		/ (GLfloat) ctx->Eval.MapGrid1un;

	gl_Begin( ctx, prim );
	for (i=i1;i<=i2;i++) {
		if (i==0) {
			u = ctx->Eval.MapGrid1u1;
		}
		else if (i==ctx->Eval.MapGrid1un) {
			u = ctx->Eval.MapGrid1u2;
		}
		else {
			u = i * du + ctx->Eval.MapGrid1u1;
		}
		gl_EvalCoord1f( ctx, u );
	}
	gl_End(ctx);
}



void gl_EvalMesh2( GLcontext* ctx, GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2 )
{
	GLint i, j;
	GLfloat u, du, v, dv, v1, v2;

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

	du = (ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1)
		/ (GLfloat) ctx->Eval.MapGrid2un;
	dv = (ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1)
		/ (GLfloat) ctx->Eval.MapGrid2vn;

#define I_TO_U( I, U )				\
	if ((I)==0) {		       	\
		U = ctx->Eval.MapGrid2u1;		\
	}					\
	else if ((I)==ctx->Eval.MapGrid2un) {	\
		U = ctx->Eval.MapGrid2u2;		\
	}					\
	else {				\
		U = (I) * du + ctx->Eval.MapGrid2u1;\
	}

#define J_TO_V( J, V )				\
	if ((J)==0) {			\
		V = ctx->Eval.MapGrid2v1;		\
	}					\
	else if ((J)==ctx->Eval.MapGrid2vn) {	\
		V = ctx->Eval.MapGrid2v2;		\
	}					\
	else {				\
		V = (J) * dv + ctx->Eval.MapGrid2v1;\
	}

	switch (mode) {
	case GL_POINT:
		gl_Begin( ctx, GL_POINTS );
		for (j=j1;j<=j2;j++) {
			J_TO_V( j, v );
			for (i=i1;i<=i2;i++) {
				I_TO_U( i, u );
				gl_EvalCoord2f( ctx, u, v );
			}
		}
		gl_End(ctx);
		break;
	case GL_LINE:
		for (j=j1;j<=j2;j++) {
			J_TO_V( j, v );
			gl_Begin( ctx, GL_LINE_STRIP );
			for (i=i1;i<=i2;i++) {
				I_TO_U( i, u );
				gl_EvalCoord2f( ctx, u, v );
			}
			gl_End(ctx);
		}
		for (i=i1;i<=i2;i++) {
			I_TO_U( i, u );
			gl_Begin( ctx, GL_LINE_STRIP );
			for (j=j1;j<=j2;j++) {
				J_TO_V( j, v );
				gl_EvalCoord2f( ctx, u, v );
			}
			gl_End(ctx);
		}
		break;
	case GL_FILL:
		for (j=j1;j<j2;j++) {
			/* NOTE: a quad strip can't be used because the four */
			/* can't be guaranteed to be coplanar! */
			gl_Begin( ctx, GL_TRIANGLE_STRIP );
			J_TO_V( j, v1 );
			J_TO_V( j+1, v2 );
			for (i=i1;i<=i2;i++) {
				I_TO_U( i, u );
				gl_EvalCoord2f( ctx, u, v1 );
				gl_EvalCoord2f( ctx, u, v2 );
			}
			gl_End(ctx);
		}
		break;
	default:
		gl_error( ctx, GL_INVALID_ENUM, "glEvalMesh2(mode)" );
		return;
	}

#undef I_TO_U
#undef J_TO_V
}

