/*
 *  10Ncurve.c
 *  Draw a NURBS curve.  The 'c' keyboard key allows you to 
 *  toggle the visibility of the control points themselves.  
 */
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>

#ifndef CALLBACK
#define CALLBACK
#endif

#define SEG  4
#define DEG  3
#define ORD  DEG+1
#define PTS  DEG+SEG
#define RDIM  3
GLfloat ctlpoints[PTS][3];
GLfloat knots[PTS+ORD];
int showPoints = 0;

GLUnurbsObj *theNurb;

/*
 *  Initializes the control points of the curve
 */
void init_curve(void)
{
    int u, m;
    ctlpoints[0][0] = -1; ctlpoints[0][1] = -1;
    ctlpoints[1][0] =  1; ctlpoints[1][1] = -1;
    ctlpoints[2][0] =  1; ctlpoints[2][1] =  1;
    ctlpoints[3][0] = -1; ctlpoints[3][1] =  1;
    for (u = 0; u < PTS; u++) 
	ctlpoints[u][2] = 0.0;
    for (u=4; u< PTS;u++)
	for (m=0; m<RDIM; m++)
	    ctlpoints[u][m] = ctlpoints[u-4][m];
   for (u=0; u< PTS+ORD; u++)
       knots[u] = u*1.0;
}				

void CALLBACK nurbsError(GLenum errorCode)
{
   const GLubyte *estring;

   estring = gluErrorString(errorCode);
   fprintf (stderr, "Nurbs Error: %s\n", estring);
   exit (0);
}
			
void init(void)
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   init_curve();

   theNurb = gluNewNurbsRenderer();
   gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 25.0);
   gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
   gluNurbsCallback(theNurb, GLU_ERROR, nurbsError);
}

void display(void)
{
   int i, j;

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glColor3f(1.0, 1.0, 1.0);
   gluBeginCurve(theNurb);
   gluNurbsCurve(theNurb, PTS+ORD, knots, RDIM, &ctlpoints[0][0], 
                   ORD, GL_MAP1_VERTEX_3);
   gluEndCurve(theNurb);

   if (showPoints) {
      glPointSize(5.0);
      glColor3f(1.0, 1.0, 0.0);
      glBegin(GL_POINTS);
      for (i = 0; i < PTS; i++) 
	  glVertex3f(ctlpoints[i][0], ctlpoints[i][1], ctlpoints[i][2]);
      glEnd();
      glBegin(GL_LINE_STRIP);
      for (i = 0; i < PTS; i++) 
	  glVertex3f(ctlpoints[i][0], ctlpoints[i][1], ctlpoints[i][2]);
      glEnd();
   }
   glFlush();
}

void reshape(int w, int h)
{
   /* just use generic instead of this */
   glViewport(0, 0, (GLsizei) w, (GLsizei) h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective (45.0, (GLdouble)w/(GLdouble)h, 3.0, 8.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef (0.0, 0.0, -5.0);
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 'c':
      case 'C':
         showPoints = !showPoints;
         glutPostRedisplay();
         break;
      case 27:
         exit(0);
         break;
      default:
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
   glutInitWindowSize (500, 500);
   glutInitWindowPosition (100, 100);
   glutCreateWindow(argv[0]);
   init();
   glutReshapeFunc(reshape);
   glutDisplayFunc(display);
   glutKeyboardFunc (keyboard);
   glutMainLoop();
   return 0; 
}

