lines.c
contents ::
  bresline.c
  lines.c
  Makefile

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

#include <stdio.h>
//#include <stdlib.h>
//#include <math.h>
//#include <string.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;


/*
**  GLOBAL VARIABLES
*/

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


// We will be collecting pairs of points, to draw lines between.
// points[0] == first point
// points[1] == second point
GLintPoint  points[2];                // the pair of points

// pointCounter will cycle between the two points (see myMouse())
int         pointCounter = 0;         // will have the values 0 and 1.
/*
** A note: while 'points' is used globally; 'pointCounter' is only
**         used in the function 'myMouse()'.
**         If we were to declare pointCounter in the function - it would
**         'reinitialised' everytime the function is called -- but
**         in  C  you can declare a variable inside a function, so
**         that it will remember its 'old' value whenever it returns!
**
** static int pointCounter = 0;
**
**         So - it's like it's a global variable - but you can only use
**         it in the function where it's declared!
**         This is both good, and bad.
**         (I've just noticed we used this in lab 1!! in myMouse()   ).
**
** Also:   We update pointCounter by letting it equal (++pointCounter)%2;
**         We could have used  pointCounter = !pointCounter;
**         which would treat it like a boolean (which C doesn't have).
*/


// We'll set the default '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}};

// What type of line should we draw (value modified in myKeyboard()).
//  -- 1 == basic line
//  -- 2 == DDA
//  -- 3 == Big DDA
//  -- 4 == Bresenham
int lineType = 1;

/*
** 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 x0, GLint y0, GLint x1, GLint y1);

/* line drawing functions */
void drawLineBasic      (GLint x0, GLint y0, GLint x1, GLint y1);
void drawLineDDA        (GLint x0, GLint y0, GLint x1, GLint y1);
void drawBigLineDDA     (GLint x0, GLint y0, GLint x1, GLint y1);
void drawLineBresenham  (GLint x0, GLint y0, GLint x1, GLint y1);

/* display functions */
void myDisplay(void);
void drawTheLines(void);

/* listeners */
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);

/* initialisers */
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);
  glLineWidth(size);
}

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

void drawLine(GLint x0, GLint y0, GLint x1, GLint y1)
{
  glBegin(GL_LINES);
  glVertex2i(x0, y0);
  glVertex2i(x1, y1);
  glEnd();
}


/*
** LINE DRAWING FUNCTIONS
*/

// Draw a line - using a dumb algorithm
void drawLineBasic(GLint x0, GLint y0, GLint x1, GLint y1)
{
  GLfloat slope;
  GLint x, y;

  slope = ((GLfloat)(y1 - y0)) / ((GLfloat)(x1 - x0));
  for (x = x0; x <= x1; x++)
  {
    y = y0 + (GLint) (slope*(x - x0));
    drawPoint(x,y);
  }
}

// Draw a line - using the basic DDA algorithm
void drawLineDDA(GLint x0, GLint y0, GLint x1, GLint y1)
{
  GLfloat slope, y;
  GLint x;

  slope = ((GLfloat)(y1 - y0)) / ((GLfloat)(x1 - x0));
  y = y0;
 
  for (x = x0; x <= x1; x++)
  {
    drawPoint(x, (GLint) y);
    y += slope;
  }
}

/*
** Draw a line - using the DDA algorithm in a 15 point grid
**
** Not quite the same thing we want to do for Bresenham's algorithm.
*/
#define gridSize 15
void drawBigLineDDA(GLint x0, GLint y0, GLint x1, GLint y1)
{
  GLfloat slope, y;
  GLint newx, newy;
  GLint oldx = x0;
  GLint oldy = y0;

  slope = ((GLfloat)(y1 - y0)) / ((GLfloat)(x1 - x0));
  y = y0;

  for (newx = x0; newx <= x1; newx+=gridSize)
  {
    newy = y0 + gridSize*( (GLint) (y-y0) / gridSize);
    drawLine(oldx, oldy, newx, newy);
    oldx = newx;
    oldy = newy;
    y += gridSize*slope;
  }
}

// Draw a line using a version of Bresenham's algorithm
//
// Pseudo code (taken from lectures) is..
/*
d := rx div 2;
incr := (rx - ry);
for i := 1 to rx do
  begin
    x := x+1;
    if d < ry then
    begin
      y := y+1;
      d := d+incr;
    end
    else d := d-ry;
  end;
  pixel[x, y] := colour;
end;
*/
void drawLineBresenham(int x0, int y0, int x1, int y1)
{
  
  int rx = x1 - x0;           // The difference in the x's
  int ry = y1 - y0;           // The difference in the y's
  int y = y0;                     // Start y off at the first pixel value
  int x = x0,i;
  int incr = rx / 2;          // The starting value for the numerator
  int d = (rx - ry);

  for (i = 1; i < rx; i++,x++){  
    if(d < ry){
      y++;
      d+=incr;
    }else d = d-ry;
    drawPoint(x,y);
  }
  /* write the code to go here.
  **
  ** Note - we want to simulate a line plotter, so rather
  ** that drawing single points when we move either across
  ** or diagonally up, we want to draw lines of 'length 1cm'
  ** for which we'll use 15 pixels.  (A 15 pixel grid)
  */
}


/*
** Main Drawing Routines
*/

/* myDisplay()  is the display callback function - it shows a blank screen */
void myDisplay(void)
{
  clearScreen();
  showScreen();
}

/* drawTheLines() is called from the mouse listener myMouse() */
void drawTheLines(void)
{
  // draw the two end points (use a thick red dot)
  // have moved this code to myMouse() -- where it belongs
  /*
  setPenColour(colour[3]);
  setPenSize(4)
  drawPoint(points[0].x, points[0].y);
  drawPoint(points[1].x, points[1].y);
  */

  // change the pen settings before calling the line drawing function
  setPenSize(1);
  setPenColour(colour[1]);

  switch (lineType)
  {
    case 1:
      drawLineBasic(points[0].x, points[0].y, points[1].x, points[1].y);
      break;
    case 2:
      drawLineDDA(points[0].x, points[0].y, points[1].x, points[1].y);
      break;
    case 3:
      setPenSize(3); // use a slightly thicker pen for this one.
      drawBigLineDDA(points[0].x, points[0].y, points[1].x, points[1].y);
      break;
    case 4:
      drawLineBresenham(points[0].x, points[0].y, points[1].x, points[1].y);
      break;
    default : break;
  }

}


/*
**  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'
    //
    if (pointCounter == 0) printf("\n");
    printf("point %d is at (%d, %d)\n", pointCounter, x, windowHeight - y - 1);

    points[pointCounter].x = x;              // x value is the mouse position
    points[pointCounter].y = windowHeight - y - 1; // flip the y coordinate

    setPenColour(colour[3]);
    setPenSize(4);
    drawPoint(points[pointCounter].x, points[pointCounter].y);

    pointCounter = (++pointCounter)%2;       // num = (num + 1) mod 2;

    if (pointCounter == 0) drawTheLines();
  }
}

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

  switch (theKey)
  {
    case '1':
    case '2':
    case '3':
    case '4': lineType = (int) theKey - (int) '0';
              break;


   // 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 an 'r' - redraw 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 27:    // ESC key.
    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.
  GLfloat  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 (%d.0, %d.0)\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);
  setPenColour(colour[0]);
  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,   windowWidth, 0,   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);
  glutReshapeFunc(myReshape);

  myInit();
  glutMainLoop();
}








James Little