/****************************************************************************
 * file:      o07.c
 * status:    04-apr-2001, pfk.
 *            12-Oct-2010, pfk:  English version.
 * function:  Help for assignment about NURBS.
 *            Generation op a circle using NURBS.
 ****************************************************************************/

#include <GL/glut.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define WINSIZE_X    600  /* horizontal window size in pixels      */
#define WINSIZE_Y    600  /* vertical window size in pixels        */
#define WINPOS_X     100  /* pixel position of window left  corner */
#define WINPOS_Y     100  /* pixel position of window upper corner */

#define EXTREME      2.0
#define XMIN    -EXTREME  /* boundaries of user coordinates */
#define XMAX     EXTREME
#define YMIN    -EXTREME
#define YMAX     EXTREME
#define ZMIN    -EXTREME
#define ZMAX     EXTREME

#define FRAME_TITLE    "Sample program using NURBS"

#define NUMCTLPOINTS   7  /* number of control points */
#define ORDER          3  /* order */
#define LENKNOTVECTOR  (NUMCTLPOINTS + ORDER)  /* length knotvector */
#define STRIDE         3  /* values per control point: X, Y, weight */

/* Knotvector: 5 knots, so 4 segments (of equal reach). */
GLfloat knotvector[LENKNOTVECTOR] = {0.00, 0.00, 0.00,  /* knot 1, 3-fold */
                                     0.25,              /* knot 2 */
                                     0.50, 0.50,        /* knot 3, 2-fold */
                                     0.75,              /* knot 4 */
                                     1.00, 1.00, 1.00}; /* knot 5, 3-fold */

/* Control points, per point: X, Y, weight. */
GLfloat ctlPoints[NUMCTLPOINTS*STRIDE] = { 1.0,  0.0,  1.0,
                                           1.0,  1.0,  0.5,
                                          -1.0,  1.0,  0.5,
                                          -1.0,  0.0,  1.0,
                                          -1.0, -1.0,  0.5,
                                           1.0, -1.0,  0.5,
                                           1.0,  0.0,  1.0};

/* NURBS object identifier. */
GLUnurbsObj *theNurbs;


/****************************************************************************
 * Initialize.
 ****************************************************************************/
void progInit (void) 
{
  /* Set background (black). */
  glClearColor (0.0, 0.0, 0.0, 0.0);

  /* Instructions for user. */
  printf ("\n%s\nType 'e', 'q' of 's' in the OpenGL window to stop.\n",
          FRAME_TITLE);

  /* Initialize NURBS curve. */
  theNurbs = gluNewNurbsRenderer ();
  gluNurbsProperty (theNurbs, GLU_SAMPLING_TOLERANCE, 5.0);
}


/****************************************************************************
 * Print text.
 ****************************************************************************/
void printText (float x, float y, char s[])
{
  int i, length;
  char c;

  glRasterPos2f (x, y);

  length = strlen (s);
  for(i=0; i<length; i++)
  { c = s[i];
    glutBitmapCharacter (GLUT_BITMAP_8_BY_13, c);
  }
}


/****************************************************************************
 * Draw line from (x1,y1,z1) to (x2,y2,z2) in user coordinates.
 ****************************************************************************/
void drawLine (float x1, float y1, float z1, float x2, float y2, float z2)
{
  glBegin (GL_LINES);
    glVertex3f (x1, y1, z1);
    glVertex3f (x2, y2, z2);
  glEnd ();
}


/****************************************************************************
 * Draw objects.
 ****************************************************************************/
void progDisplay (void)
{
  glClear (GL_COLOR_BUFFER_BIT);

  /* Draw white X- and Y-axes. */
  glColor3f (1.0, 1.0, 1.0);
  drawLine (XMIN, 0.0, 0.0, XMAX, 0.0, 0.0);
  drawLine (0.0, YMIN, 0.0, 0.0, YMAX, 0.0);

  glColor3f (1.0, 1.0, 1.0);
  printText (XMIN+0.2, YMIN+0.4, "Type 'e', 'q', 's' or <ESC> in");
  printText (XMIN+0.2, YMIN+0.2, "this window to stop.");

  /* Draw NURBS curve. */
  gluBeginCurve (theNurbs);
    gluNurbsCurve (theNurbs, LENKNOTVECTOR, knotvector, STRIDE, ctlPoints,
                   ORDER, GL_MAP1_VERTEX_3);
  gluEndCurve (theNurbs);

  glFlush ();
}


/****************************************************************************
 * Handle change in size of output window:
 *   "w" and "h" are the new width and height of the output window in window 
 *   coordinates,
 *   "shift_x" and "shift_y" are the shifts in X and Y in user coordinates.
 ****************************************************************************/
void progReshape (GLsizei w, GLsizei h)
{
  float shift_x, shift_y;

  shift_x = ( (float)(w-WINSIZE_X) / (float)WINSIZE_X ) * (XMAX-XMIN);
  shift_y = ( (float)(h-WINSIZE_Y) / (float)WINSIZE_Y ) * (YMAX-YMIN);

  glViewport (0, 0, w, h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  /* Original size of objects will be kept. */
  gluOrtho2D (XMIN, XMAX + shift_x, YMIN, YMAX + shift_y);

  glFlush ();
}


/****************************************************************************
 * Handle keyboard interrupts:
 * terminate program if "e", "q", "s" or ESC is entered in the OpenGL window.
 ****************************************************************************/
void progKeyboard (unsigned char key, int x, int y)
{
  switch (key)   // only the following keys are allowed
  { case 'e': 
    case 'q': 
    case 's': 
    case  27: exit (0);  // escape key ESC
              break;
  }
}


/****************************************************************************
 * Main program function.
 ****************************************************************************/
int main (int argc, char** argv)
{
  int windowID;

  /* Initialize OpenGL Utility Toolkit. */
  glutInit (&argc, argv);

  /* Create output window. */
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize (WINSIZE_X, WINSIZE_Y); 
  glutInitWindowPosition (WINPOS_X, WINPOS_Y);
  windowID = glutCreateWindow (FRAME_TITLE);

  /* Event handling functions. */
  glutDisplayFunc (progDisplay); 
  glutReshapeFunc (progReshape);
  glutKeyboardFunc (progKeyboard);

  progInit ();

  /* Event handling loop. */
  glutMainLoop ();

  return 0;
}
