lines2.c
contents ::
  lines2.c
  Makefile
  m3

/*
** 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 integers.
*/
typedef struct
{
  GLint x, y;
} GLintPoint;

/*
** ... 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;
  GLintPoint pt[ARRAY_MAX_NUM];

} GLintPointArray;


/*
** 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  _GLintPointPtr
{
  GLintPoint         point;
  struct        _GLintPointPtr  *next;
} GLintPointPtr;


/*
**  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.
GLintPointArray  poly = {0};


// This will be our linked list of points
GLintPointPtr  *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(int size);

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

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

/* utility functions for using linked lists */
GLintPointPtr* clearPoints(GLintPointPtr* points);
void           clearPoints2(GLintPointPtr* *points);
void           clearPoints3(GLintPointPtr* *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(int size)
{
  glPointSize(size);
}

void drawPoint(GLint x, GLint y)
{
  glBegin(GL_POINTS);
    glVertex2i(x, y);
  glEnd();
}

void drawLine(GLint x1, GLint y1, GLint x2, GLint y2)
{
  glBegin(GL_LINES);
    glVertex2i(x1, y1);
    glVertex2i(x2, y2);
  glEnd();
}


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

/*
**  The C version to read a file of lines
**
**  NOTE:  This is *not* good code!!  (Not robust)
*/
void drawPolyLineFile(char * filename)
{
  int i, j, numpolys, numlines;
  GLint x, y;
  char* line;
  FILE *inputFile;

  if ((inputFile = fopen(filename, "r")) == NULL) // Can't open the file!!
  {
    printf("\n\nCannot open the requested file '%s'\n\n", filename);
    printf("Program exiting");
    exit(1);
  }

  // We read a string (upto 10 characters) from the file, and make it an int
  fgets(line, 10, inputFile);
  numpolys = atoi(line);

  for (j = 0; j < numpolys; j++)
  {
    fgets(line, 10, inputFile);
    numlines = atoi(line);

    glBegin(GL_LINE_STRIP);
      for (i = 0; i < numlines; i++)
      {
         fgets(line, 10, inputFile);
         sscanf(line, "%d %d", &x, &y);  // scans the string 'line' for two ints
         glVertex2i(x,y);
      }
    glEnd();
  }

  fclose(inputFile);
  showScreen();
}

/* The C++ Version of drawing from the file
**
**  NOTE:  This is *not* good code!!  (Not robust)
**  needs the extra include:
**
**  #include <fstream.h>
**
* /
void drawPolyLineFile(char * filename)
{
  fstream inStream;
  inStream.open(filename, ios ::in);
  if (inStream.fail())
    return;

  GLint numpolys, numlines, x, y;
  inStream >> numpolys;
  for (int j = 0; j < numpolys; j++)
  {
    inStream>> numlines;
    glBegin(GL_LINE_STRIP);
    for (int i = 0; i < numlines; i++)
    {
      inStream >> x >> y;
      glVertex2i(x,y);
    }
    glEnd();
  }
  inStream.close();
  showScreen();
}
/* End of the C++ version */



/*
**  ------------------------
**  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);
**
*/
GLintPointPtr* clearPoints(GLintPointPtr* points)
{
  GLintPointPtr  *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(GLintPointPtr* *points)
{
  GLintPointPtr  **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(GLintPointPtr* *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;
              break;

    // if we type an 'f' - draw the file
    case 'f':
    case 'F': drawPolyLineFile("polydata.txt");
              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