lines.c |
| /* ** 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 |