lines2.c
contents ::
  Makefile
  lines2.c

/*
** Includes
*/
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>


/*
** TYPE DECLARATIONS
*/

/*
** this a record structure that contains two GL doubles.
*/
typedef struct
{
  GLdouble x, y;
} GLPoint;

/*
** ... and this is a record of three GL floats (reals) for colour.
**     r, g, and b  can have the real values between 0 and 1.
*/
typedef struct
{
  GLfloat r, g, b;
} GLfloatRGBColour;


/*
** We define a structure to store an array of points
**
** <Variable>.num     == the number of points stored
** <Variable>.pt[0]   == the first point in the list (if any)
** <Variable>.pt[0].x == the x-coord of the first point in the list (if any)
*/
#define ARRAY_MAX_NUM  10
typedef struct
{
  int num;
  GLPoint pt[ARRAY_MAX_NUM];

} GLPointArray;


/*
** Data structure for Linked List of Points
**
** It contains two fields:
** <Variable>.point  (which has two fields itself -- .x, and .y)
** <Variable>.next
*/
typedef struct  _GLPointPtr
{
  GLPoint         point;
  struct        _GLPointPtr  *next;
} GLPointPtr;


/*
**  GLOBAL VARIABLES
*/

// windowHeight is used for 'flipping' the y-position.
GLint windowHeight;
GLint windowWidth;


// We will be collecting a list of points, to draw lines between.
// We initialise it so that poly.num == 0.
GLPointArray  poly = {0};


// This will be our linked list of points
GLPointPtr  *points;


// We'll set the 'penColour' to 0 (black)
int penColour = 0;

// This is a list of 8 colours (Black -> White)
// example use:   setColour(colour[3]);
GLfloatRGBColour colour[8] = { {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f},
                                  {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f},
                               {0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 1.0f},
                               {1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};

/*
** FUNCTION HEADERS
*/

/* utility functions for setting up the window */
void setWindow  (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top);
void setViewport(GLint left, GLint right, GLint bottom, GLint top);

/* utility drawing functions */
void clearScreen(void);
void showScreen(void);
void setPenColour(GLfloatRGBColour newColour);
void setPenSize(GLfloat size);

void drawPoint(GLdouble x, GLdouble y);
void drawLine(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);

/* utility functions for using polylines */
void drawPolyLine(GLPointArray poly, int closed);
void drawPolyLineFile(char * filename);

/* utility functions for using linked lists */
GLPointPtr* clearPoints(GLPointPtr* points);
void        clearPoints2(GLPointPtr* *points);
void        clearPoints3(GLPointPtr* *points);

/* callback functions */
void myDisplay(void);
void myMouse(int button, int state, int x, int y);
void myKeyboard(unsigned char theKey, int mouseX, int mouseY);
void myReshape(GLsizei W, GLsizei H);

void myInit(void);
int  main (int argc,  char** argv);


/*
** UTILITY FUNCTIONS
*/

/* set up the world view (2d - Orthographic) */
void setWindow(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(left, right, bottom, top);
}

/* set up the viewing window */
void setViewport(GLint left, GLint right, GLint bottom, GLint top)
{
  glViewport(left, bottom, right - left, top - bottom);
}


void clearScreen(void)
{
  glClear(GL_COLOR_BUFFER_BIT);
}

void showScreen(void)
{
  glFlush();
}

void setPenColour(GLfloatRGBColour newColour)
{
  glColor3f(newColour.r, newColour.g, newColour.b);
}

void setPenSize(GLfloat size)
{
  glPointSize(size);
  glLineWidth(size);
}

void drawPoint(GLdouble x, GLdouble y)
{
  glBegin(GL_POINTS);
    glVertex2d(x, y);
  glEnd();
}

void drawLine(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2)
{
  glBegin(GL_LINES);
    glVertex2d(x1, y1);
    glVertex2d(x2, y2);
  glEnd();
}


/*
** POLYLINE DRAWING FUNCTION
*/
void drawPolyLine(GLPointArray poly, int closed)
{
  int i;
  glBegin(closed ? GL_LINE_LOOP : GL_LINE_STRIP);
  for (i = 0; i < poly.num; i++)
    glVertex2d(poly.pt[i].x, poly.pt[i].y);
  glEnd();
}


/*
**  ------------------------
**  Routines for Linked List
**  ------------------------
*/


/*
** Remove all the points in the list, and free up the space
**
** This is the first attempt, must be called like:
**   points = clearPoints(points);
**
*/
GLPointPtr* clearPoints(GLPointPtr* points)
{
  GLPointPtr  *tempptr;
  while (points != NULL)
  {
    tempptr = points;
    points  = points->next;
    free(tempptr);
  }
  return NULL;
}


/*
** Remove all the points in the list, and free up the space
**
** This is the second attempt, as above, except it uses pass by
** reference rather than pass by value, it must be called like:
**   clearPoints2(&points);
**
*/
void clearPoints2(GLPointPtr* *points)
{
  GLPointPtr  **tempptr;
  while (*points != NULL)
  {
    *tempptr = *points;
    *points  = (*points)->next;
    free(*tempptr);
  }
}

/*
** Remove all the points in the list, and free up the space
**
** This is the third attempt, as above, except it uses recursion
** to free the list.  Note that free() does not make the value NULL
** so we must do that ourselves (why don't we above :).
**   clearPoints3(&points);
**
*/
void clearPoints3(GLPointPtr* *points)
{
  if (*points == NULL) return;

  clearPoints3(& ((*points)->next) );
  free(*points);
  *points = NULL;

}


/*
** Main Drawing Routine
*/

void myDisplay(void)
{
  clearScreen();
  drawPolyLine(poly, 0);
  showScreen();
}


/*
**  LISTENERS
*/

/*
**  Mouse Listener
*/
void myMouse(int button, int state, int x, int y)
{
  // if we click the LEFT button - keep a track of the points.
  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
  {
    // This is just for diagnostics !!
    //
    // %d == print the corresponding value as an integer.
    // \n == print 'end of line'
    //
    printf("Clicked on point (%d, %d)\n", x, windowHeight - y - 1);

    if (poly.num >= ARRAY_MAX_NUM) // Too many points for the array!
    {
      printf("TOO MANY POINTS!!\n");
      return;
    }
    poly.pt[poly.num].x = x;
    poly.pt[poly.num].y = windowHeight - y - 1;
    poly.num++;
    drawPolyLine(poly, 0);
  }
  else if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)
  {
    drawPolyLine(poly, 1);
  }
}

/*
**  Key Listener
*/
void myKeyboard(unsigned char theKey, int mouseX, int mouseY)
{
  mouseY = windowHeight - mouseY - 1;

  switch (theKey)
  {

   // if we type a 'c' - cycle through the 7 visible colours
    case 'c':
    case 'C': penColour = (++penColour)%7;
              setPenColour(colour[penColour]);
              break;

    // if we type a 'd' - draw the polyline
    case 'd':
    case 'D': drawPolyLine(poly, 0);
              break;

    // if we type an 'e' - erase the list of points
    case 'e':
    case 'E': poly.num = 0;
              clearPoints3(&points);
              break;

    // if we type an 'r' - refresh the screen
    case 'r':
    case 'R': myDisplay();
              break;

    // if we type a 'w' - clear the screen
    case 'w':
    case 'W': clearScreen();
              showScreen();
              break;

    // if we type a 'q' - quit the program
    case 'q':
    case 'Q': exit(0);

    // otherwise - we don't have anything to do.
    default: break;
  }
}

/*
** Listener for reshaping the window
*/
void myReshape(GLsizei W, GLsizei H)
{
  windowWidth  = W;
  windowHeight = H;

  setWindow  (0.0, windowWidth, 0.0, windowHeight);
  setViewport(0,   windowWidth, 0,   windowHeight);
}



/*
** INITIALISERS
*/
void myInit(void)
{
  // Here's something to work out the size of the computer screen
  // and the size of the window we've created.
  float screenWidth, screenHeight;
  screenWidth  = glutGet(GLUT_SCREEN_WIDTH);
  screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
  windowWidth  = glutGet(GLUT_WINDOW_WIDTH);
  windowHeight = glutGet(GLUT_WINDOW_HEIGHT);
  printf("\nInitial values are: Screen (%.1f, %.1f) && Window (%.1f, %.1f)\n",
          screenWidth, screenHeight, windowWidth, windowHeight);
  // now let's return to what we want to do..

  // The 'usual' initialisation..
  glClearColor(1.0, 1.0, 1.0, 0.0);
  glColor3f(0.0f, 0.0f, 0.0f);
  setPenSize(2.0);

  // ..and for what we're doing, we want the Viewport to match the World Window
  setWindow  (0.0, windowWidth, 0.0, windowHeight);
  setViewport(0, (GLint) windowWidth, 0, (GLint) windowHeight);
}



/*
**  MAIN
*/
int main (int argc,  char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize(640, 480);
  glutInitWindowPosition(100, 150);
  glutCreateWindow("Lines - type q to QUIT");
  glutDisplayFunc(myDisplay);
  glutMouseFunc(myMouse);
  glutKeyboardFunc(myKeyboard);
  myInit();
  glutMainLoop();
}


James Little