text: /* $Id: nurbssrf.c,v 1.1 1998-10-05 22:35:18 cg Exp $ */
  
  /*
   * Mesa 3-D graphics library
   * Version:  2.4
   * Copyright (C) 1995-1997  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.
   */
  
  
  /*
   * $Log: nurbssrf.c,v $
   * Revision 1.1  1998-10-05 22:35:18  cg
   * *** empty log message ***
   *
   * Revision 1.7  1997/07/24 01:28:44  brianp
   * changed precompiled header symbol from PCH to PC_HEADER
   *
   * Revision 1.6  1997/06/23 00:22:07  brianp
   * include <string.h>
   *
   * Revision 1.5  1997/05/28 02:29:38  brianp
   * added support for precompiled headers (PCH), inserted APIENTRY keyword
   *
   * Revision 1.4  1997/05/27 03:20:35  brianp
   * minor clean-up
   *
   * Revision 1.3  1997/05/27 03:00:16  brianp
   * incorporated Bogdan's new NURBS code
   *
   * Revision 1.2  1996/09/27 23:13:02  brianp
   * added return 0 to get_surface_dim() to silence warning
   *
   * Revision 1.1  1996/09/27 01:19:39  brianp
   * Initial revision
   *
   */
  
  
  /*
   * NURBS implementation written by Bogdan Sikorski (bogdan@cira.it)
   * See README2 for more info.
   */
  
  
  #ifdef PC_HEADER
  #include "all.h"
  #else
  #include <math.h>
  #include <stdlib.h>
  #include <string.h>
  #include "gluP.h"
  #include "nurbs.h"
  #endif
  
  
  static int
  get_surface_dim(GLenum type)
  {
  	switch(type)
  	{
  		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:  abort();  /* TODO: is this OK? */
  	}
          return 0; /*never get here*/
  }
  
  static GLenum
  test_nurbs_surface(GLUnurbsObj *nobj, surface_attribs *attrib)
  {
  	GLenum err;
  	GLint tmp_int;
  
  	if(attrib->sorder < 0 || attrib->torder < 0)
  	{
  		call_user_error(nobj,GLU_INVALID_VALUE);
  		return GLU_ERROR;
  	}
  	glGetIntegerv(GL_MAX_EVAL_ORDER,&tmp_int);
  	if(attrib->sorder > tmp_int || attrib->sorder < 2)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR1);
  		return GLU_ERROR;
  	}
  	if(attrib->torder > tmp_int || attrib->torder < 2)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR1);
  		return GLU_ERROR;
  	}
  	if(attrib->sknot_count < attrib->sorder +2)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR2);
  		return GLU_ERROR;
  	}
  	if(attrib->tknot_count < attrib->torder +2)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR2);
  		return GLU_ERROR;
  	}
  	if(attrib->s_stride < 0 || attrib->t_stride < 0)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR34);
  		return GLU_ERROR;
  	}
  	if(attrib->sknot==NULL || attrib->tknot==NULL || attrib->ctrlarray==NULL)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR36);
  		return GLU_ERROR;
  	}
  	if((err=test_knot(attrib->tknot_count,attrib->tknot,attrib->torder))
  		!=GLU_NO_ERROR)
  	{
  		call_user_error(nobj,err);
  		return GLU_ERROR;
  	}
  	if((err=test_knot(attrib->sknot_count,attrib->sknot,attrib->sorder))
  		!=GLU_NO_ERROR)
  	{
  		call_user_error(nobj,err);
  		return GLU_ERROR;
  	}
  	return GLU_NO_ERROR;
  }
  
  static GLenum
  test_nurbs_surfaces(GLUnurbsObj *nobj)
  {
  	/* test the geometric data */
  	if(test_nurbs_surface(nobj,&(nobj->surface.geom))!=GLU_NO_ERROR)
  		return GLU_ERROR;
  	/* now test the attributive data */
  	/* color */
  	if(nobj->surface.color.type!=GLU_INVALID_ENUM)
  		if(test_nurbs_surface(nobj,&(nobj->surface.color))!=GLU_NO_ERROR)
  			return GLU_ERROR;
  	/* normal */
  	if(nobj->surface.normal.type!=GLU_INVALID_ENUM)
  		if(test_nurbs_surface(nobj,&(nobj->surface.normal))!=GLU_NO_ERROR)
  			return GLU_ERROR;
  	/* texture */
  	if(nobj->surface.texture.type!=GLU_INVALID_ENUM)
  		if(test_nurbs_surface(nobj,&(nobj->surface.texture))!=GLU_NO_ERROR)
  			return GLU_ERROR;
  	return GLU_NO_ERROR;
  }
  
  static GLenum
  convert_surf(knot_str_type *s_knot, knot_str_type *t_knot,
  	surface_attribs *attrib, GLfloat **new_ctrl,
  	GLint *s_n_ctrl, GLint *t_n_ctrl)
  {
  	GLfloat **tmp_ctrl;
  	GLfloat *ctrl_offset;
  	GLint tmp_n_control;
  	GLint i,j,t_cnt,s_cnt;
  	GLint tmp_stride;
  	GLint dim;
  	GLenum err;
  
  	/* valid range is empty? */
  	if((s_knot->unified_knot !=NULL && s_knot->unified_nknots==0) || 
  		(t_knot->unified_knot !=NULL && t_knot->unified_nknots==0))
  	{
  		if(s_knot->unified_knot)
  		{
  			free(s_knot->unified_knot);
  			s_knot->unified_knot=NULL;
  		}
  		if(t_knot->unified_knot)
  		{
  			free(t_knot->unified_knot);
  			t_knot->unified_knot=NULL;
  		}
  		*s_n_ctrl=0;
  		*t_n_ctrl=0;
  		return GLU_NO_ERROR;
  	}
  	t_cnt=attrib->tknot_count-attrib->torder;
  	s_cnt=attrib->sknot_count-attrib->sorder;
  	if((tmp_ctrl=(GLfloat **)malloc(sizeof(GLfloat *)*t_cnt))==NULL)
  		return GLU_OUT_OF_MEMORY;
  	if((err=explode_knot(s_knot))!=GLU_NO_ERROR)
  	{
  		free(tmp_ctrl);
  		if(s_knot->unified_knot)
  		{
  			free(s_knot->unified_knot);
  			s_knot->unified_knot=NULL;
  		}
  		return err;
  	}
  	if(s_knot->unified_knot)
  	{
  		free(s_knot->unified_knot);
  		s_knot->unified_knot=NULL;
  	}
  	if((err=calc_alphas(s_knot))!=GLU_NO_ERROR)
  	{
  		free(tmp_ctrl);
  		free(s_knot->new_knot);
  		return err;
  	}
  	free(s_knot->new_knot);
  	ctrl_offset=attrib->ctrlarray;
  	dim=attrib->dim;
  	for(i=0;i<t_cnt;i++)
  	{
  		if((err=calc_new_ctrl_pts(ctrl_offset,attrib->s_stride,s_knot,
  			dim,&(tmp_ctrl[i]),&tmp_n_control))!=GLU_NO_ERROR)
  		{
  			for(--i;i<=0;i--)
  				free(tmp_ctrl[i]);
  			free(tmp_ctrl);
  			free(s_knot->alpha);
  			return err;
  		}
  		ctrl_offset+=attrib->t_stride;
  	}
  	free(s_knot->alpha);
  	tmp_stride=dim*tmp_n_control;
  	if((*new_ctrl=(GLfloat *)malloc(sizeof(GLfloat)*tmp_stride*t_cnt))
  		==NULL)
  	{
  		for(i=0;i<t_cnt;i++)
  			free(tmp_ctrl[i]);
  		free(tmp_ctrl);
  		return GLU_OUT_OF_MEMORY;
  	}
  	for(i=0;i<tmp_n_control;i++)
  		for(j=0;j<t_cnt;j++)
  			MEMCPY(*new_ctrl+j*dim+i*dim*t_cnt,tmp_ctrl[j]+dim*i,
  				sizeof(GLfloat)*dim);
  	for(i=0;i<t_cnt;i++)
  		free(tmp_ctrl[i]);
  	free(tmp_ctrl);
  	*s_n_ctrl=tmp_n_control;
  	
  	if((tmp_ctrl=(GLfloat **)malloc(sizeof(GLfloat *)*(*s_n_ctrl)))==NULL)
  	{
  		return GLU_OUT_OF_MEMORY;
  	}
  	if((err=explode_knot(t_knot))!=GLU_NO_ERROR)
  	{
  		free(tmp_ctrl);
  		if(t_knot->unified_knot)
  		{
  			free(t_knot->unified_knot);
  			t_knot->unified_knot=NULL;
  		}
  		return err;
  	}
  	if(t_knot->unified_knot)
  	{
  		free(t_knot->unified_knot);
  		t_knot->unified_knot=NULL;
  	}
  	if((err=calc_alphas(t_knot))!=GLU_NO_ERROR)
  	{
  		free(tmp_ctrl);
  		free(t_knot->new_knot);
  		return err;
  	}
  	free(t_knot->new_knot);
  	ctrl_offset=*new_ctrl;
  	for(i=0;i<(*s_n_ctrl);i++)
  	{
  		if((err=calc_new_ctrl_pts(ctrl_offset,dim,t_knot,
  			dim,&(tmp_ctrl[i]),&tmp_n_control))!=GLU_NO_ERROR)
  		{
  			for(--i;i<=0;i--)
  				free(tmp_ctrl[i]);
  			free(tmp_ctrl);
  			free(t_knot->alpha);
  			return err;
  		}
  		ctrl_offset+=dim*t_cnt;
  	}
  	free(t_knot->alpha);
  	free(*new_ctrl);
  	tmp_stride=dim*tmp_n_control;
  	if((*new_ctrl=(GLfloat *)malloc(sizeof(GLfloat)*tmp_stride*(*s_n_ctrl)))
  		==NULL)
  	{
  		for(i=0;i<(*s_n_ctrl);i++)
  			free(tmp_ctrl[i]);
  		free(tmp_ctrl);
  		return GLU_OUT_OF_MEMORY;
  	}
  	for(i=0;i<(*s_n_ctrl);i++)
  	{
  		MEMCPY(*new_ctrl+i*tmp_stride,tmp_ctrl[i],sizeof(GLfloat)*tmp_stride);
  		free(tmp_ctrl[i]);
  	}
  	free(tmp_ctrl);
  	*t_n_ctrl=tmp_n_control;
  	return GLU_NO_ERROR;
  }
  
  /* prepare the knot information structures */
  static GLenum
  fill_knot_structures(GLUnurbsObj *nobj,
  	knot_str_type *geom_s_knot, knot_str_type *geom_t_knot,
  	knot_str_type *color_s_knot, knot_str_type *color_t_knot,
  	knot_str_type *normal_s_knot,  knot_str_type *normal_t_knot,
  	knot_str_type *texture_s_knot, knot_str_type *texture_t_knot)
  {
  	GLint order;
  	GLfloat *knot;
  	GLint nknots;
  	GLint t_min,t_max;
  
  	geom_s_knot->unified_knot=NULL;
  	knot=geom_s_knot->knot=nobj->surface.geom.sknot;
  	nknots=geom_s_knot->nknots=nobj->surface.geom.sknot_count;
  	order=geom_s_knot->order=nobj->surface.geom.sorder;
  	geom_s_knot->delta_nknots=0;
  	t_min=geom_s_knot->t_min=order-1;
  	t_max=geom_s_knot->t_max=nknots-order;
  	if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR3);
  		return GLU_ERROR;
  	}
  	if(fabs(knot[0]-knot[t_min])<EPSILON)
  	{
  		/* knot open at beggining */
  		geom_s_knot->open_at_begin=GL_TRUE;
  	}
  	else
  		geom_s_knot->open_at_begin=GL_FALSE;
  	if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  	{
  		/* knot open at end */
  		geom_s_knot->open_at_end=GL_TRUE;
  	}
  	else
  		geom_s_knot->open_at_end=GL_FALSE;
  	geom_t_knot->unified_knot=NULL;
  	knot=geom_t_knot->knot=nobj->surface.geom.tknot;
  	nknots=geom_t_knot->nknots=nobj->surface.geom.tknot_count;
  	order=geom_t_knot->order=nobj->surface.geom.torder;
  	geom_t_knot->delta_nknots=0;
  	t_min=geom_t_knot->t_min=order-1;
  	t_max=geom_t_knot->t_max=nknots-order;
  	if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  	{
  		call_user_error(nobj,GLU_NURBS_ERROR3);
  		return GLU_ERROR;
  	}
  	if(fabs(knot[0]-knot[t_min])<EPSILON)
  	{
  		/* knot open at beggining */
  		geom_t_knot->open_at_begin=GL_TRUE;
  	}
  	else
  		geom_t_knot->open_at_begin=GL_FALSE;
  	if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  	{
  		/* knot open at end */
  		geom_t_knot->open_at_end=GL_TRUE;
  	}
  	else
  		geom_t_knot->open_at_end=GL_FALSE;
  
  	if(nobj->surface.color.type!=GLU_INVALID_ENUM)
  	{
  		color_s_knot->unified_knot=(GLfloat *)1;
  		knot=color_s_knot->knot=nobj->surface.color.sknot;
  		nknots=color_s_knot->nknots=nobj->surface.color.sknot_count;
  		order=color_s_knot->order=nobj->surface.color.sorder;
  		color_s_knot->delta_nknots=0;
  		t_min=color_s_knot->t_min=order-1;
  		t_max=color_s_knot->t_max=nknots-order;
  		if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  		{
  			call_user_error(nobj,GLU_NURBS_ERROR3);
  			return GLU_ERROR;
  		}
  		if(fabs(knot[0]-knot[t_min])<EPSILON)
  		{
  			/* knot open at beggining */
  			color_s_knot->open_at_begin=GL_TRUE;
  		}
  		else
  			color_s_knot->open_at_begin=GL_FALSE;
  		if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  		{
  			/* knot open at end */
  			color_s_knot->open_at_end=GL_TRUE;
  		}
  		else
  			color_s_knot->open_at_end=GL_FALSE;
  		color_t_knot->unified_knot=(GLfloat *)1;
  		knot=color_t_knot->knot=nobj->surface.color.tknot;
  		nknots=color_t_knot->nknots=nobj->surface.color.tknot_count;
  		order=color_t_knot->order=nobj->surface.color.torder;
  		color_t_knot->delta_nknots=0;
  		t_min=color_t_knot->t_min=order-1;
  		t_max=color_t_knot->t_max=nknots-order;
  		if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  		{
  			call_user_error(nobj,GLU_NURBS_ERROR3);
  			return GLU_ERROR;
  		}
  		if(fabs(knot[0]-knot[t_min])<EPSILON)
  		{
  			/* knot open at beggining */
  			color_t_knot->open_at_begin=GL_TRUE;
  		}
  		else
  			color_t_knot->open_at_begin=GL_FALSE;
  		if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  		{
  			/* knot open at end */
  			color_t_knot->open_at_end=GL_TRUE;
  		}
  		else
  			color_t_knot->open_at_end=GL_FALSE;
  	}
  	else
  	{
  		color_s_knot->unified_knot=NULL;
  		color_t_knot->unified_knot=NULL;
  	}
  
  	if(nobj->surface.normal.type!=GLU_INVALID_ENUM)
  	{
  		normal_s_knot->unified_knot=(GLfloat *)1;
  		knot=normal_s_knot->knot=nobj->surface.normal.sknot;
  		nknots=normal_s_knot->nknots=nobj->surface.normal.sknot_count;
  		order=normal_s_knot->order=nobj->surface.normal.sorder;
  		normal_s_knot->delta_nknots=0;
  		t_min=normal_s_knot->t_min=order-1;
  		t_max=normal_s_knot->t_max=nknots-order;
  		if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  		{
  			call_user_error(nobj,GLU_NURBS_ERROR3);
  			return GLU_ERROR;
  		}
  		if(fabs(knot[0]-knot[t_min])<EPSILON)
  		{
  			/* knot open at beggining */
  			normal_s_knot->open_at_begin=GL_TRUE;
  		}
  		else
  			normal_s_knot->open_at_begin=GL_FALSE;
  		if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  		{
  			/* knot open at end */
  			normal_s_knot->open_at_end=GL_TRUE;
  		}
  		else
  			normal_s_knot->open_at_end=GL_FALSE;
  		normal_t_knot->unified_knot=(GLfloat *)1;
  		knot=normal_t_knot->knot=nobj->surface.normal.tknot;
  		nknots=normal_t_knot->nknots=nobj->surface.normal.tknot_count;
  		order=normal_t_knot->order=nobj->surface.normal.torder;
  		normal_t_knot->delta_nknots=0;
  		t_min=normal_t_knot->t_min=order-1;
  		t_max=normal_t_knot->t_max=nknots-order;
  		if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  		{
  			call_user_error(nobj,GLU_NURBS_ERROR3);
  			return GLU_ERROR;
  		}
  		if(fabs(knot[0]-knot[t_min])<EPSILON)
  		{
  			/* knot open at beggining */
  			normal_t_knot->open_at_begin=GL_TRUE;
  		}
  		else
  			normal_t_knot->open_at_begin=GL_FALSE;
  		if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  		{
  			/* knot open at end */
  			normal_t_knot->open_at_end=GL_TRUE;
  		}
  		else
  			normal_t_knot->open_at_end=GL_FALSE;
  	}
  	else
  	{
  		normal_s_knot->unified_knot=NULL;
  		normal_t_knot->unified_knot=NULL;
  	}
  
  	if(nobj->surface.texture.type!=GLU_INVALID_ENUM)
  	{
  		texture_s_knot->unified_knot=(GLfloat *)1;
  		knot=texture_s_knot->knot=nobj->surface.texture.sknot;
  		nknots=texture_s_knot->nknots=nobj->surface.texture.sknot_count;
  		order=texture_s_knot->order=nobj->surface.texture.sorder;
  		texture_s_knot->delta_nknots=0;
  		t_min=texture_s_knot->t_min=order-1;
  		t_max=texture_s_knot->t_max=nknots-order;
  		if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  		{
  			call_user_error(nobj,GLU_NURBS_ERROR3);
  			return GLU_ERROR;
  		}
  		if(fabs(knot[0]-knot[t_min])<EPSILON)
  		{
  			/* knot open at beggining */
  			texture_s_knot->open_at_begin=GL_TRUE;
  		}
  		else
  			texture_s_knot->open_at_begin=GL_FALSE;
  		if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  		{
  			/* knot open at end */
  			texture_s_knot->open_at_end=GL_TRUE;
  		}
  		else
  			texture_s_knot->open_at_end=GL_FALSE;
  		texture_t_knot->unified_knot=(GLfloat *)1;
  		knot=texture_t_knot->knot=nobj->surface.texture.tknot;
  		nknots=texture_t_knot->nknots=nobj->surface.texture.tknot_count;
  		order=texture_t_knot->order=nobj->surface.texture.torder;
  		texture_t_knot->delta_nknots=0;
  		t_min=texture_t_knot->t_min=order-1;
  		t_max=texture_t_knot->t_max=nknots-order;
  		if(fabs(knot[t_min]-knot[t_max])<EPSILON)
  		{
  			call_user_error(nobj,GLU_NURBS_ERROR3);
  			return GLU_ERROR;
  		}
  		if(fabs(knot[0]-knot[t_min])<EPSILON)
  		{
  			/* knot open at beggining */
  			texture_t_knot->open_at_begin=GL_TRUE;
  		}
  		else
  			texture_t_knot->open_at_begin=GL_FALSE;
  		if(fabs(knot[t_max]-knot[nknots-1])<EPSILON)
  		{
  			/* knot open at end */
  			texture_t_knot->open_at_end=GL_TRUE;
  		}
  		else
  			texture_t_knot->open_at_end=GL_FALSE;
  	}
  	else
  	{
  		texture_s_knot->unified_knot=NULL;
  		texture_t_knot->unified_knot=NULL;
  	}
  	return GLU_NO_ERROR;
  }
  
  void
  free_new_ctrl(new_ctrl_type *p)
  {
  	if(p->geom_ctrl)
  		free(p->geom_ctrl);
  	if(p->geom_offsets)
  		free(p->geom_offsets);
  	if(p->color_ctrl)
  	{
  		free(p->color_ctrl);
  		if(p->color_offsets)
  			free(p->color_offsets);
  	}
  	if(p->normal_ctrl)
  	{
  		free(p->normal_ctrl);
  		if(p->normal_offsets)
  			free(p->normal_offsets);
  	}
  	if(p->texture_ctrl)
  	{
  		free(p->texture_ctrl);
  		if(p->texture_offsets)
  			free(p->texture_offsets);
  	}
  }
  
  /* convert surfaces - geometry and possible attribute ones into equivalent */
  /* sequence of adjacent Bezier patches */
  static GLenum
  convert_surfs(GLUnurbsObj *nobj, new_ctrl_type *new_ctrl)
  {
  	knot_str_type	geom_s_knot,color_s_knot,normal_s_knot,texture_s_knot;
  	knot_str_type	geom_t_knot,color_t_knot,normal_t_knot,texture_t_knot;
  	GLenum			err;
  
  	if((err=fill_knot_structures(nobj,&geom_s_knot,&geom_t_knot,
  		&color_s_knot,&color_t_knot,&normal_s_knot,&normal_t_knot,
  		&texture_s_knot,&texture_t_knot)) !=GLU_NO_ERROR)
  	{
  		return err;
  	}
  	/* unify knots - all knots should have the same working range */
  	if((err=select_knot_working_range(nobj,&geom_s_knot,&color_s_knot,
  		&normal_s_knot,&texture_s_knot)) !=GLU_NO_ERROR)
  	{
  		call_user_error(nobj,err);
  		return err;
  	}
  	if((err=select_knot_working_range(nobj,&geom_t_knot,&color_t_knot,
  		&normal_t_knot,&texture_t_knot)) !=GLU_NO_ERROR)
  	{
  		free_unified_knots(&geom_s_knot,&color_s_knot,&normal_s_knot,
  			&texture_s_knot);
  		call_user_error(nobj,err);
  		return err;
  	}
  
  	/* convert the geometry surface */
  	nobj->surface.geom.dim=get_surface_dim(nobj->surface.geom.type);
  	if((err=convert_surf(&geom_s_knot,&geom_t_knot,&(nobj->surface.geom),
  		&(new_ctrl->geom_ctrl),&(new_ctrl->geom_s_pt_cnt),
  		&(new_ctrl->geom_t_pt_cnt)))!=GLU_NO_ERROR)
  	{
  		free_unified_knots(&geom_s_knot,&color_s_knot,&normal_s_knot,
  			&texture_s_knot);
  		free_unified_knots(&geom_t_knot,&color_t_knot,&normal_t_knot,
  			&texture_t_knot);
  		call_user_error(nobj,err);
  		return err;
  	}
  	/* if additional attributive surfaces are given convert them as well */
  	if(color_s_knot.unified_knot)
  	{
  		nobj->surface.color.dim=get_surface_dim(nobj->surface.color.type);
  		if((err=convert_surf(&color_s_knot,&color_t_knot,&(nobj->surface.color),
  			&(new_ctrl->color_ctrl),&(new_ctrl->color_s_pt_cnt),
  			&(new_ctrl->color_t_pt_cnt)))!=GLU_NO_ERROR)
  		{
  			free_unified_knots(&color_s_knot,&color_s_knot,&normal_s_knot,
  				&texture_s_knot);
  			free_unified_knots(&color_t_knot,&color_t_knot,&normal_t_knot,
  				&texture_t_knot);
  			free_new_ctrl(new_ctrl);
  			call_user_error(nobj,err);
  			return err;
  		}
  	}
  	if(normal_s_knot.unified_knot)
  	{
  		nobj->surface.normal.dim=get_surface_dim(nobj->surface.normal.type);
  		if((err=convert_surf(&normal_s_knot,&normal_t_knot,
  			&(nobj->surface.normal),
  			&(new_ctrl->normal_ctrl),&(new_ctrl->normal_s_pt_cnt),
  			&(new_ctrl->normal_t_pt_cnt)))!=GLU_NO_ERROR)
  		{
  			free_unified_knots(&normal_s_knot,&normal_s_knot,&normal_s_knot,
  				&texture_s_knot);
  			free_unified_knots(&normal_t_knot,&normal_t_knot,&normal_t_knot,
  				&texture_t_knot);
  			free_new_ctrl(new_ctrl);
  			call_user_error(nobj,err);
  			return err;
  		}
  	}
  	if(texture_s_knot.unified_knot)
  	{
  		nobj->surface.texture.dim=get_surface_dim(nobj->surface.texture.type);
  		if((err=convert_surf(&texture_s_knot,&texture_t_knot,
  			&(nobj->surface.texture),
  			&(new_ctrl->texture_ctrl),&(new_ctrl->texture_s_pt_cnt),
  			&(new_ctrl->texture_t_pt_cnt)))!=GLU_NO_ERROR)
  		{
  			free_unified_knots(&texture_s_knot,&texture_s_knot,&texture_s_knot,
  				&texture_s_knot);
  			free_unified_knots(&texture_t_knot,&texture_t_knot,&texture_t_knot,
  				&texture_t_knot);
  			free_new_ctrl(new_ctrl);
  			call_user_error(nobj,err);
  			return err;
  		}
  	}
  	return GLU_NO_ERROR;
  }
  
  /* tesselate the "boundary" Bezier edge strips */
  void
  tesselate_strip_t_line(GLint top_start,GLint top_end,GLint top_z,
  	GLint bottom_start,GLint bottom_end,GLint bottom_z,GLint bottom_domain)
  {
  	GLint top_cnt,bottom_cnt,tri_cnt,k;
  	GLint direction;
  
  	top_cnt=top_end-top_start;
  	direction= (top_cnt>=0 ? 1: -1);
  	bottom_cnt=bottom_end-bottom_start;
  	glBegin(GL_LINES);
  	while(top_cnt)
  	{
  		if(bottom_cnt)
  			tri_cnt=top_cnt/bottom_cnt;
  		else
  			tri_cnt=abs(top_cnt);
  		for(k=0;k<=tri_cnt;k++ , top_start+=direction)
  		{
  			glEvalCoord2f((GLfloat)bottom_z/bottom_domain,
  				(GLfloat)bottom_start/bottom_domain);
  			glEvalPoint2(top_z,top_start);
  		}
  		if(bottom_cnt)
  		{
  			glEvalCoord2f((GLfloat)bottom_z/bottom_domain,
  				(GLfloat)bottom_start/bottom_domain);
  			bottom_start+=direction;
  			top_start-=direction;
  			glEvalCoord2f((GLfloat)bottom_z/bottom_domain,
  				(GLfloat)bottom_start/bottom_domain);
  			glEvalCoord2f((GLfloat)bottom_z/bottom_domain,
  				(GLfloat)bottom_start/bottom_domain);
  			glEvalPoint2(top_z,top_start);
  		}
  		top_cnt-=direction*tri_cnt;
  		bottom_cnt-=direction;
  	}
  	glEnd();
  }
  
  void
  tesselate_strip_t_fill(GLint top_start,GLint top_end,GLint top_z,
  	GLint bottom_start,GLint bottom_end,GLint bottom_z,GLint bottom_domain)
  {
  	GLint top_cnt,bottom_cnt,tri_cnt,k;
  	GLint direction;
  
  	top_cnt=top_end-top_start;
  	direction= (top_cnt>=0 ? 1: -1);
  	bottom_cnt=bottom_end-bottom_start;
  	while(top_cnt)
  	{
  		if(bottom_cnt)
  			tri_cnt=top_cnt/bottom_cnt;
  		else
  			tri_cnt=abs(top_cnt);
  		glBegin(GL_TRIANGLE_FAN);
  		glEvalCoord2f((GLfloat)bottom_z/bottom_domain,
  			(GLfloat)bottom_start/bottom_domain);
  		for(k=0;k<=tri_cnt;k++ , top_start+=direction)
  			glEvalPoint2(top_z,top_start);
  		if(bottom_cnt)
  		{
  			bottom_start+=direction;
  			top_start-=direction;
  			glEvalCoord2f((GLfloat)bottom_z/bottom_domain,
  				(GLfloat)bottom_start/bottom_domain);
  		}
  		glEnd();
  		top_cnt-=direction*tri_cnt;
  		bottom_cnt-=direction;
  	}
  }
  
  void
  tesselate_strip_t(GLenum display_mode, GLint top_start, GLint top_end, 
  	GLint top_z, GLint bottom_start, GLint bottom_end, GLint bottom_z, 
  	GLint bottom_domain)
  {
  	if(display_mode==GL_FILL)
  		tesselate_strip_t_fill(top_start,top_end,top_z,bottom_start,
  			bottom_end,bottom_z,bottom_domain);
  	else
  		tesselate_strip_t_line(top_start,top_end,top_z,bottom_start,
  			bottom_end,bottom_z,bottom_domain);
  }
  	
  
  void
  tesselate_strip_s_fill(GLint top_start, GLint top_end, GLint top_z,
  	GLint bottom_start, GLint bottom_end, GLint bottom_z, GLfloat bottom_domain)
  {
  	GLint top_cnt,bottom_cnt,tri_cnt,k;
  	GLint direction;
  
  	top_cnt=top_end-top_start;
  	direction= (top_cnt>=0 ? 1: -1);
  	bottom_cnt=bottom_end-bottom_start;
  	while(top_cnt)
  	{
  		if(bottom_cnt)
  			tri_cnt=top_cnt/bottom_cnt;
  		else
  			tri_cnt=abs(top_cnt);
  		glBegin(GL_TRIANGLE_FAN);
  		glEvalCoord2f((GLfloat)bottom_start/bottom_domain,
  			(GLfloat)bottom_z/bottom_domain);
  		for(k=0;k<=tri_cnt;k++ , top_start+=direction)
  			glEvalPoint2(top_start,top_z);
  		if(bottom_cnt)
  		{
  			bottom_start+=direction;
  			top_start-=direction;
  			glEvalCoord2f((GLfloat)bottom_start/bottom_domain,
  				(GLfloat)bottom_z/bottom_domain);
  		}
  		glEnd();
  		top_cnt-=direction*tri_cnt;
  		bottom_cnt-=direction;
  	}
  }
  
  void
  tesselate_strip_s_line(GLint top_start, GLint top_end, GLint top_z,
  	GLint bottom_start, GLint bottom_end, GLint bottom_z, GLfloat bottom_domain)
  {
  	GLint top_cnt,bottom_cnt,tri_cnt,k;
  	GLint direction;
  
  	top_cnt=top_end-top_start;
  	direction= (top_cnt>=0 ? 1: -1);
  	bottom_cnt=bottom_end-bottom_start;
  	glBegin(GL_LINES);
  	while(top_cnt)
  	{
  		if(bottom_cnt)
  			tri_cnt=top_cnt/bottom_cnt;
  		else
  			tri_cnt=abs(top_cnt);
  		for(k=0;k<=tri_cnt;k++ , top_start+=direction)
  		{
  			glEvalCoord2f((GLfloat)bottom_start/bottom_domain,
  				(GLfloat)bottom_z/bottom_domain);
  			glEvalPoint2(top_start,top_z);
  		}
  		if(bottom_cnt)
  		{
  			glEvalCoord2f((GLfloat)bottom_start/bottom_domain,
  				(GLfloat)bottom_z/bottom_domain);
  			bottom_start+=direction;
  			top_start-=direction;
  			glEvalCoord2f((GLfloat)bottom_start/bottom_domain,
  				(GLfloat)bottom_z/bottom_domain);
  			glEvalPoint2(top_start,top_z);
  			glEvalCoord2f((GLfloat)bottom_start/bottom_domain,
  				(GLfloat)bottom_z/bottom_domain);
  		}
  		top_cnt-=direction*tri_cnt;
  		bottom_cnt-=direction;
  	}
  	glEnd();
  }
  
  void
  tesselate_strip_s(GLenum display_mode, GLint top_start, GLint top_end,
  	GLint top_z, GLint bottom_start, GLint bottom_end, GLint bottom_z,
  	GLfloat bottom_domain)
  {
  	if(display_mode==GL_FILL)
  		tesselate_strip_s_fill(top_start,top_end,top_z,bottom_start,
  			bottom_end,bottom_z,bottom_domain);
  	else
  		tesselate_strip_s_line(top_start,top_end,top_z,bottom_start,
  			bottom_end,bottom_z,bottom_domain);
  }
  
  void
  tesselate_bottom_left_corner(GLenum display_mode, GLfloat s_1, GLfloat t_1)
  {
  	if(display_mode==GL_FILL)
  	{
  		glBegin(GL_TRIANGLE_FAN);
  		glEvalPoint2(1,1);
  		glEvalCoord2f(s_1,0.0);
  		glEvalCoord2f(0.0,0.0);
  		glEvalCoord2f(0.0,t_1);
  	}
  	else
  	{
  		glBegin(GL_LINES);
  		glEvalCoord2f(0.0,0.0);
  		glEvalCoord2f(0.0,t_1);
  		glEvalCoord2f(0.0,0.0);
  		glEvalPoint2(1,1);
  		glEvalCoord2f(0.0,0.0);
  		glEvalCoord2f(s_1,0.0);
  	}
  	glEnd();
  }
  
  void
  tesselate_bottom_right_corner(GLenum display_mode, GLint v_top,GLint v_bottom,
  	GLfloat s_1, GLfloat t_1)
  {
  	if(display_mode==GL_FILL)
  	{
  		glBegin(GL_TRIANGLE_FAN);
  		glEvalPoint2(1,v_top);
  		glEvalCoord2f(0.0,v_bottom*t_1);
  		glEvalCoord2f(0.0,(v_bottom+1)*t_1);
  		glEvalCoord2f(s_1,(v_bottom+1)*t_1);
  	}
  	else
  	{
  		glBegin(GL_LINES);
  		glEvalCoord2f(0.0,(v_bottom+1)*t_1);
  		glEvalPoint2(1,v_top);
  		glEvalCoord2f(0.0,(v_bottom+1)*t_1);
  		glEvalCoord2f(0.0,v_bottom*t_1);
  		glEvalCoord2f(0.0,(v_bottom+1)*t_1);
  		glEvalCoord2f(s_1,(v_bottom+1)*t_1);
  	}
  	glEnd();
  }
  
  void
  tesselate_top_left_corner(GLenum display_mode, GLint u_right, GLint u_left,
  	GLfloat s_1, GLfloat t_1)
  {
  	if(display_mode==GL_FILL)
  	{
  		glBegin(GL_TRIANGLE_FAN);
  		glEvalPoint2(u_right,1);
  		glEvalCoord2f((u_left+1)*s_1,t_1);
  		glEvalCoord2f((u_left+1)*s_1,0.0);
  		glEvalCoord2f(u_left*s_1,0.0);
  	}
  	else
  	{
  		glBegin(GL_LINES);
  		glEvalCoord2f((u_left+1)*s_1,0.0);
  		glEvalPoint2(u_right,1);
  		glEvalCoord2f((u_left+1)*s_1,0.0);
  		glEvalCoord2f(u_left*s_1,0.0);
  		glEvalCoord2f((u_left+1)*s_1,0.0);
  		glEvalCoord2f((u_left+1)*s_1,t_1);
  	}
  	glEnd();
  }
  
  void
  tesselate_top_right_corner(GLenum display_mode, GLint u_left, GLint v_bottom,
  	GLint u_right, GLint v_top, GLfloat s_1, GLfloat t_1)
  {
  	if(display_mode==GL_FILL)
  	{
  		glBegin(GL_TRIANGLE_FAN);
  		glEvalPoint2(u_left,v_bottom);
  		glEvalCoord2f((u_right-1)*s_1,v_top*t_1);
  		glEvalCoord2f(u_right*s_1,v_top*t_1);
  		glEvalCoord2f(u_right*s_1,(v_top-1)*t_1);
  	}
  	else
  	{
  		glBegin(GL_LINES);
  		glEvalCoord2f(u_right*s_1,v_top*t_1);
  		glEvalPoint2(u_left,v_bottom);
  		glEvalCoord2f(u_right*s_1,v_top*t_1);
  		glEvalCoord2f(u_right*s_1,(v_top-1)*t_1);
  		glEvalCoord2f(u_right*s_1,v_top*t_1);
  		glEvalCoord2f((u_right-1)*s_1,v_top*t_1);
  	}
  	glEnd();
  }
  
  /* do mesh mapping of Bezier */
  static void
  nurbs_map_bezier(GLenum display_mode,GLint *sfactors,GLint *tfactors,
  	GLint s_bezier_cnt, GLint t_bezier_cnt, GLint s, GLint t)
  {
  	GLint		top,bottom,right,left;
  
  
  	if(s==0)
  	{
  		top=*(tfactors+t*3);
  		bottom=*(tfactors+t*3+1);
  	}
  	else
  	if(s==s_bezier_cnt-1)
  	{
  		top=*(tfactors+t*3+2);
  		bottom=*(tfactors+t*3);
  	}
  	else
  	{
  		top=bottom=*(tfactors+t*3);
  	}
  	if(t==0)
  	{
  		left=*(sfactors+s*3+1);
  		right=*(sfactors+s*3);
  	}
  	else
  	if(t==t_bezier_cnt-1)
  	{
  		left=*(sfactors+s*3);
  		right=*(sfactors+s*3+2);
  	}
  	else
  	{
  		left=right=*(sfactors+s*3);
  	}
  
  	if(top>bottom)
  	{
  		if(left<right)
  		{
  			glMapGrid2f(right, 0.0, 1.0, top, 0.0, 1.0);
  			glEvalMesh2(display_mode,1,right, 1, top);
  			tesselate_strip_s(display_mode,1,right,1,1,left,0,(GLfloat)left);
  			tesselate_bottom_left_corner(display_mode,(GLfloat)(1.0/left),
  				(GLfloat)(1.0/bottom));
  /*			tesselate_strip_t(display_mode,1,top,1,1,bottom,0,(GLfloat)bottom);*/
  			tesselate_strip_t(display_mode,top,1,1,bottom,1,0,(GLfloat)bottom);
  		}
  		else
  		if(left==right)
  		{
  			glMapGrid2f(right, 0.0, 1.0, top, 0.0, 1.0);
  			glEvalMesh2(display_mode,1,right, 0, top);
  /*			tesselate_strip_t(display_mode,0,top,1,0,bottom,0,(GLfloat)bottom);*/
  			tesselate_strip_t(display_mode,top,0,1,bottom,0,0,(GLfloat)bottom);
  		}
  		else
  		{
  			glMapGrid2f(left, 0.0, 1.0, top, 0.0, 1.0);
  			glEvalMesh2(display_mode,1,left, 0, top-1);
  /*			tesselate_strip_t(display_mode,0,top-1,1,0,bottom-1,0,
  				(GLfloat)bottom);*/
  			tesselate_strip_t(display_mode,top-1,0,1,bottom-1,0,0,
  				(GLfloat)bottom);
  			tesselate_bottom_right_corner(display_mode,top-1,bottom-1,
  				(GLfloat)(1.0/right),(GLfloat)(1.0/bottom));
  /*			tesselate_strip_s(display_mode,1,left,top-1,1,right,right,
  				(GLfloat)right);*/
  			tesselate_strip_s(display_mode,left,1,top-1,right,1,right,
  				(GLfloat)right);
  		}
  	}
  	else
  	if(top==bottom)
  	{
  		if(left<right)
  		{
  			glMapGrid2f(right, 0.0, 1.0, top, 0.0, 1.0);
  			glEvalMesh2(display_mode,0,right, 1, top);
  			tesselate_strip_s(display_mode,0,right,1,0,left,0,(GLfloat)left);
  		}
  		else
  		if(left==right)
  		{
  			glMapGrid2f(right, 0.0, 1.0, top, 0.0, 1.0);
  			glEvalMesh2(display_mode,0,right, 0, top);
  		}
  		else
  		{
  			glMapGrid2f(left, 0.0, 1.0, top, 0.0, 1.0);
  			glEvalMesh2(display_mode,0,left, 0, top-1);
  /*			tesselate_strip_s(display_mode,0,left,top-1,0,right,right,
  				(GLfloat)right);*/
  			tesselate_strip_s(display_mode,left,0,top-1,right,0,right,
  				(GLfloat)right);
  		}
  	}
  	else
  	{
  		if(left<right)
  		{
  			glMapGrid2f(right, 0.0, 1.0, bottom, 0.0, 1.0);
  			glEvalMesh2(display_mode,0,right-1, 1, bottom);
  			tesselate_strip_s(display_mode,0,right-1,1,0,left-1,0,
  				(GLfloat)left);
  			tesselate_top_left_corner(display_mode,right-1,left-1,
  				(GLfloat)(1.0/left),(GLfloat)(1.0/top));
  			tesselate_strip_t(display_mode,1,bottom,right-1,1,top,top,
  				(GLfloat)top);
  		}
  		else
  		if(left==right)
  		{
  			glMapGrid2f(right, 0.0, 1.0, bottom, 0.0, 1.0);
  			glEvalMesh2(display_mode,0,right-1, 0, bottom);
  			tesselate_strip_t(display_mode,0,bottom,right-1,0,top,top,
  				(GLfloat)top);
  		}
  		else
  		{
  			glMapGrid2f(left, 0.0, 1.0, bottom, 0.0, 1.0);
  			glEvalMesh2(display_mode,0,left-1, 0, bottom-1);
  			tesselate_strip_t(display_mode,0,bottom-1,left-1,0,top-1,top,
  				(GLfloat)top);
  			tesselate_top_right_corner(display_mode,left-1,bottom-1,right,top,
  				(GLfloat)(1.0/right),(GLfloat)(1.0/top));
  /*			tesselate_strip_s(display_mode,0,left-1,bottom-1,0,right-1,right,
  				(GLfloat)right);*/
  			tesselate_strip_s(display_mode,left-1,0,bottom-1,right-1,0,right,
  				(GLfloat)right);
  		}
  	}
  }
  
  /* draw NURBS surface in OUTLINE POLYGON mode */
  static void
  draw_polygon_mode( GLenum display_mode, GLUnurbsObj *nobj,
  	new_ctrl_type *new_ctrl, GLint *sfactors, GLint *tfactors )
  {
  	GLsizei				offset;
  	GLint				t_bezier_cnt,s_bezier_cnt;
  	GLboolean			do_color,do_normal,do_texture;
  	GLint				i,j;
  
  	t_bezier_cnt=new_ctrl->t_bezier_cnt;
  	s_bezier_cnt=new_ctrl->s_bezier_cnt;
  	glEnable(nobj->surface.geom.type);
  	if(new_ctrl->color_ctrl)
  	{
  		glEnable(nobj->surface.color.type);
  		do_color=GL_TRUE;
  	}
  	else
  		do_color=GL_FALSE;
  	if(new_ctrl->normal_ctrl)
  	{
  		glEnable(nobj->surface.normal.type);
  		do_normal=GL_TRUE;
  	}
  	else
  		do_normal=GL_FALSE;
  	if(new_ctrl->texture_ctrl)
  	{
  		glEnable(nobj->surface.texture.type);
  		do_texture=GL_TRUE;
  	}
  	else
  		do_texture=GL_FALSE;
  	for(j=0; j<s_bezier_cnt; j++)
  	{
  		for(i=0; i<t_bezier_cnt; i++)
  		{
  			offset=j*t_bezier_cnt + i;
  			if(fine_culling_test_3D(nobj,*(new_ctrl->geom_offsets + offset),
  					nobj->surface.geom.sorder,nobj->surface.geom.torder,
  					new_ctrl->geom_s_stride,new_ctrl->geom_t_stride,
  					nobj->surface.geom.dim))
  				continue;
  			glMap2f(nobj->surface.geom.type,0.0,1.0,new_ctrl->geom_s_stride,
  				nobj->surface.geom.sorder,0.0,1.0,new_ctrl->geom_t_stride,
  				nobj->surface.geom.torder,*(new_ctrl->geom_offsets + offset));
  			if(do_color)
  			{
  				glMap2f(nobj->surface.color.type,0.0,1.0,
  					new_ctrl->color_s_stride,nobj->surface.color.sorder,
  					0.0,1.0,new_ctrl->color_t_stride,nobj->surface.color.torder,
  					*(new_ctrl->color_offsets + offset));
  			}
  			if(do_normal)
  			{
  				glMap2f(nobj->surface.normal.type,0.0,1.0,
  					new_ctrl->normal_s_stride,nobj->surface.normal.sorder,
  					0.0,1.0,new_ctrl->normal_t_stride,
  					nobj->surface.normal.torder,
  					*(new_ctrl->normal_offsets+offset));
  			}
  			if(do_texture)
  			{
  				glMap2f(nobj->surface.texture.type,0.0,1.0,
  					new_ctrl->texture_s_stride,nobj->surface.texture.sorder,
  					0.0,1.0,new_ctrl->texture_t_stride,
  					nobj->surface.texture.torder,
  					*(new_ctrl->texture_offsets+offset));
  			}
  /*			glMapGrid2f(sfactors[j*3+0],0.0,1.0,tfactors[i*3+0],0.0,1.0);
  			glEvalMesh2(display_mode,0,sfactors[j*3+0],0,tfactors[i*3+0]);*/
  			nurbs_map_bezier(display_mode,sfactors,tfactors,s_bezier_cnt,
  				t_bezier_cnt,j,i);
  		}
  	}
  }
  
  
  
  /* draw NURBS surface in OUTLINE POLYGON mode */
  #if 0
  static void
  draw_patch_mode( GLenum display_mode, GLUnurbsObj *nobj,
  	new_ctrl_type *new_ctrl, GLint *sfactors, GLint *tfactors )
  {
  	GLsizei				offset;
  	GLint				t_bezier_cnt,s_bezier_cnt;
  	GLboolean			do_color,do_normal,do_texture;
  	GLint				i,j;
  
  	t_bezier_cnt=new_ctrl->t_bezier_cnt;
  	s_bezier_cnt=new_ctrl->s_bezier_cnt;
  	glEnable(nobj->surface.geom.type);
  	if(new_ctrl->color_ctrl)
  	{
  		glEnable(nobj->surface.color.type);
  		do_color=GL_TRUE;
  	}
  	else
  		do_color=GL_FALSE;
  	if(new_ctrl->normal_ctrl)
  	{
  		glEnable(nobj->surface.normal.type);
  		do_normal=GL_TRUE;
  	}
  	else
  		do_normal=GL_FALSE;
  	if(new_ctrl->texture_ctrl)
  	{
  		glEnable(nobj->surface.texture.type);
  		do_texture=GL_TRUE;
  	}
  	else
  		do_texture=GL_FALSE;
  	for(j=0; j<s_bezier_cnt; j++)
  	{
  		for(i=0; i<t_bezier_cnt; i++)
  		{
  			offset=j*t_bezier_cnt + i;
  			if(fine_culling_test_3D(nobj,*(new_ctrl->geom_offsets + offset),
  					nobj->surface.geom.sorder,nobj->surface.geom.torder,
  					new_ctrl->geom_s_stride,new_ctrl->geom_t_stride,
  					nobj->surface.geom.dim))
  				continue;
  			glMap2f(nobj->surface.geom.type,0.0,1.0,new_ctrl->geom_s_stride,
  				nobj->surface.geom.sorder,0.0,1.0,new_ctrl->geom_t_stride,
  				nobj->surface.geom.torder,*(new_ctrl->geom_offsets + offset));
  			if(do_color)
  			{
  				glMap2f(nobj->surface.color.type,0.0,1.0,
  					new_ctrl->color_s_stride,nobj->surface.color.sorder,
  					0.0,1.0,new_ctrl->color_t_stride,nobj->surface.color.torder,
  					*(new_ctrl->color_offsets + offset));
  			}
  			if(do_normal)
  			{
  				glMap2f(nobj->surface.normal.type,0.0,1.0,
  					new_ctrl->normal_s_stride,nobj->surface.normal.sorder,
  					0.0,1.0,new_ctrl->normal_t_stride,
  					nobj->surface.normal.torder,
  					*(new_ctrl->normal_offsets+offset));
  			}
  			if(do_texture)
  			{
  				glMap2f(nobj->surface.texture.type,0.0,1.0,
  					new_ctrl->texture_s_stride,nobj->surface.texture.sorder,
  					0.0,1.0,new_ctrl->texture_t_stride,
  					nobj->surface.texture.torder,
  					*(new_ctrl->texture_offsets+offset));
  			}
  			nurbs_map_bezier(display_mode,sfactors,tfactors,s_bezier_cnt,
  				t_bezier_cnt,i,j);
  /*			glMapGrid2f(sfactors[j],0.0,1.0,tfactors[i],0.0,1.0);
  			glEvalMesh2(display_mode,0,sfactors[j],0,tfactors[i]);*/
  		}
  	}
  }
  #endif
  
  
  
  void
  init_new_ctrl(new_ctrl_type *p)
  {
  	p->geom_ctrl=p->color_ctrl=p->normal_ctrl=p->texture_ctrl=NULL;
  	p->geom_offsets=p->color_offsets=p->normal_offsets=p->texture_offsets=NULL;
  	p->s_bezier_cnt=p->t_bezier_cnt=0;
  }
  
  GLenum
  augment_new_ctrl(GLUnurbsObj *nobj, new_ctrl_type *p)
  {
  	GLsizei offset_size;
  	GLint	i,j;
  
  	p->s_bezier_cnt=(p->geom_s_pt_cnt)/(nobj->surface.geom.sorder);
  	p->t_bezier_cnt=(p->geom_t_pt_cnt)/(nobj->surface.geom.torder);
  	offset_size=(p->s_bezier_cnt)*(p->t_bezier_cnt);
  	p->geom_t_stride=nobj->surface.geom.dim;
  	p->geom_s_stride=(p->geom_t_pt_cnt)*(nobj->surface.geom.dim);
  	p->color_t_stride=nobj->surface.color.dim;
  	p->color_s_stride=(p->color_t_pt_cnt)*(nobj->surface.color.dim);
  	p->normal_t_stride=nobj->surface.normal.dim;
  	p->normal_s_stride=(p->normal_t_pt_cnt)*(nobj->surface.normal.dim);
  	p->texture_t_stride=nobj->surface.texture.dim;
  	p->texture_s_stride=(p->texture_t_pt_cnt)*(nobj->surface.texture.dim);
  	if((p->geom_offsets=(GLfloat **)malloc(sizeof(GLfloat *)*offset_size))==NULL)
  	{
  		call_user_error(nobj,GLU_OUT_OF_MEMORY);
  		return GLU_ERROR;
  	}
  	if(p->color_ctrl)
  		if((p->color_offsets=(GLfloat **)malloc(sizeof(GLfloat *)*offset_size))==NULL)
  		{
  			free_new_ctrl(p);
  			call_user_error(nobj,GLU_OUT_OF_MEMORY);
  			return GLU_ERROR;
  		}
  	if(p->normal_ctrl)
  		if((p->normal_offsets=(GLfloat **)malloc(sizeof(GLfloat *)*offset_size))==NULL)
  		{
  			free_new_ctrl(p);
  			call_user_error(nobj,GLU_OUT_OF_MEMORY);
  			return GLU_ERROR;
  		}
  	if(p->texture_ctrl)
  		if((p->texture_offsets=(GLfloat **)malloc(sizeof(GLfloat *)*offset_size))==NULL)
  		{
  			free_new_ctrl(p);
  			call_user_error(nobj,GLU_OUT_OF_MEMORY);
  			return GLU_ERROR;
  		}
  	for(i=0;i<p->s_bezier_cnt;i++)
  		for(j=0;j<p->t_bezier_cnt;j++)
  			*(p->geom_offsets + i*(p->t_bezier_cnt) + j) =
  				p->geom_ctrl + i*(nobj->surface.geom.sorder)*
  				(nobj->surface.geom.dim)*(p->geom_t_pt_cnt) +
  				j*(nobj->surface.geom.dim)*(nobj->surface.geom.torder);
  	if(p->color_ctrl)
  		for(i=0;i<p->s_bezier_cnt;i++)
  			for(j=0;j<p->t_bezier_cnt;j++)
  				*(p->color_offsets + i*(p->t_bezier_cnt) + j) =
  					p->color_ctrl + i*(nobj->surface.color.sorder)*
  					(nobj->surface.color.dim)*(p->color_t_pt_cnt) +
  					j*(nobj->surface.color.dim)*(nobj->surface.color.torder);
  	if(p->normal_ctrl)
  		for(i=0;i<p->s_bezier_cnt;i++)
  			for(j=0;j<p->t_bezier_cnt;j++)
  				*(p->normal_offsets + i*(p->t_bezier_cnt) + j) =
  					p->normal_ctrl + i*(nobj->surface.normal.sorder)*
  					(nobj->surface.normal.dim)*(p->normal_t_pt_cnt) +
  					j*(nobj->surface.normal.dim)*(nobj->surface.normal.torder);
  	if(p->texture_ctrl)
  		for(i=0;i<p->s_bezier_cnt;i++)
  			for(j=0;j<p->t_bezier_cnt;j++)
  				*(p->texture_offsets + i*(p->t_bezier_cnt) + j) =
  					p->texture_ctrl + i*(nobj->surface.texture.sorder)*
  					(nobj->surface.texture.dim)*(p->texture_t_pt_cnt) +
  					j*(nobj->surface.texture.dim)*(nobj->surface.texture.torder);
  	return GLU_NO_ERROR;
  }
  
  /* main NURBS surface procedure */
  void
  do_nurbs_surface( GLUnurbsObj *nobj )
  {
  	GLint			*sfactors,*tfactors;
  	new_ctrl_type	new_ctrl;
  
  	/* test user supplied data */
  	if(test_nurbs_surfaces(nobj)!=GLU_NO_ERROR)
  		return;
  
  	init_new_ctrl(&new_ctrl);
  
  	if(convert_surfs(nobj,&new_ctrl)!=GLU_NO_ERROR)
  		return;
  	if(augment_new_ctrl(nobj,&new_ctrl)!=GLU_NO_ERROR)
  		return;
  	switch(nobj->sampling_method)
  	{
  		case GLU_PATH_LENGTH:
  			if(glu_do_sampling_3D(nobj,&new_ctrl,&sfactors,&tfactors)!=
  				GLU_NO_ERROR)
  			{
  				free_new_ctrl(&new_ctrl);
  				return;
  			}
  			break;
  		case GLU_DOMAIN_DISTANCE:
  			if(glu_do_sampling_uv(nobj,&new_ctrl,&sfactors,&tfactors)!=
  				GLU_NO_ERROR)
  			{
  				free_new_ctrl(&new_ctrl);
  				return;
  			}
  			break;
  		case GLU_PARAMETRIC_ERROR:
  			if(glu_do_sampling_param_3D(nobj,&new_ctrl,&sfactors,&tfactors)!=
  				GLU_NO_ERROR)
  			{
  				free_new_ctrl(&new_ctrl);
  				return;
  			}
  			break;
  		default:
  			abort();
  	}
  	glFrontFace(GL_CW);
  	switch(nobj->display_mode)
  	{
  		case GLU_FILL:
  /*			if(polygon_trimming(nobj,&new_ctrl,sfactors,tfactors)==GLU_NO_ERROR)*/
  				draw_polygon_mode(GL_FILL,nobj,&new_ctrl,sfactors,tfactors);
  			break;
  		case GLU_OUTLINE_POLYGON:
  			/* TODO - missing trimming handeling */
  /* just for now - no OUTLINE_PATCH mode 
  			draw_patch_mode(GL_LINE,nobj,&new_ctrl,sfactors,tfactors);
  			break; */
  		case GLU_OUTLINE_PATCH:
  /*			if(polygon_trimming(nobj,&new_ctrl,sfactors,tfactors)==GLU_NO_ERROR)*/
  				draw_polygon_mode(GL_LINE,nobj,&new_ctrl,sfactors,tfactors);
  			break;
  		default:
  			abort();  /* TODO: is this OK? */
  	}
  	free(sfactors);
  	free(tfactors);
  	free_new_ctrl(&new_ctrl);
  }
  
  