#include #include #include #include /* ********************************************************************* This is a solution to the CS410 programming lab 1 assignment for Fall 2004. It is an interactive demonstration of Bresenham's line drawing algorithm. Ross Beveridge September 1, 2004 */ /* ===================================================================== Global variables - yeah, this is a kind of dated style of programming these parameters configure how the GLUT window is sized, placed, and internal contents are scaled */ static int wDimPixels = 600; /* Initial size, width and height, of window */ static int startX = 50; /* Initial x position of upper left corner */ static int startY = 75; /* Initial x position of upper left corner */ static GLclampf backRed = 1.0; /* Amount of red in the background */ static GLclampf backGreen = 1.0; /* Amount of green in the background */ static GLclampf backBlue = 1.0; /* Amount of blue in the background */ static GLclampf lineRed = 0.0; /* Amount of red in standard lines */ static GLclampf lineGreen = 0.0; /* Amount of green in standard lines */ static GLclampf lineBlue = 0.0; /* Amount of blue in standard lines */ /* The lattic spacing sets up the relationship between the screen pixels and the lattice vertices (points). Spacing is explicitly set and then handy parameters such as the starting and ending pixel coordinates of lattice, along with how many lattic point are drawn, are all derived and computed when the lattice is initialized */ static const int latSpacing = 60; /* Screen pixels between lattice points */ static const int latStart = 30; static const int latCount = 10; static const int latEnd = 580; /* The next parameters control the radius and degree of fill in the lattic pixel disks */ static GLdouble pixRadius = 21.0; static GLdouble pixFills[10][10]; /* Some globals to manage the state of the mouse clicks, the first end point and second end point of a line segment */ static int px1, py1, px2, py2; static int mClickCount; /* Globals to manage the iterative Bresenham algorithm. This is awkward, but since state must be conveyed between distinct calls to the iterative portion of the algorithm, it is necessary to maintain this state information globally rather an encapsulated inside a single line drawing procedure. Line segment endpoints are orderd so x1 is equal to or less than (left of) x2. Thus dx is always positive and segments occupy only four octants, it is a bit brute force, but distinct increment variables will be defined for each of the octants. The octant number goes as follows. The octants are defined as follows: dx > 0 dy > 0 dy > dx (slope 1 to infinity) .... Octant 1 dx > 0 dy > 0 dy <= dx (slope 0 to 1) ........... Octant 2 dx > 0 dy < 0 (-dy) <= dx (slope 0 to -1) .......... Octant 3 dx > 0 dy < 0 (-dy) > dx (slope -1 to -infinity) .. Octant 4 */ static int bx1, by1, bx2, by2; /* Almost the same as px1, ... but ordered left to right */ static int bdx, bdy; /* The rise and run of the line */ static int bifa, bifb; /* The a and b coefficients of Bresenham's from lecture */ static int bG; /* The Bresenham decision variable */ static int bx, by; /* The current/last pixel filled */ static int oct2deltaE; static int oct2deltaNE; static int msecDelay = 500; /* Millisecond delay between drawing of successive pixels */ /* ===================================================================== Functions to draw "pixels" and the pixel lattice on the window */ /* Draw a simple line segment, again taken directly from the text book */ void drawLineSegment( int x1, int y1, int x2, int y2) { // printf("Drawing a line from %d %d to %d %d/n", x1, x2, y1, y2); glColor3f(lineRed, lineGreen, lineBlue); glBegin (GL_LINES); glVertex2i( x1, y1); glVertex2i( x2, y2); glEnd(); } /* Initialize the fill amount on the lattice pixels. Only do this once each time you want to clear any "lines" drawn to the display. */ void clearPixFills(){ int i, j; for (i = 0; i < latCount; i++){ for (j = 0; j < latCount; j++){ pixFills[i][j] = 0.1; } } px1 = 0; py1 = 0; px2 = 0; py2 = 0; mClickCount = 0; } /* Draw a pixel icon as a circle. It is placed at position x y. The fill parameter runs between 0.0 and 1.0. Values toward 0.0 means the disk is not filled. A value of 1.0 means it is entirely filled */ void drawLatticePixel( int i, int j, GLfloat red, GLfloat green, GLfloat blue){ GLdouble dx, dy; dx = (i * latSpacing) + latStart; dy = (j * latSpacing) + latStart; glColor3f (red, green, blue); GLUquadricObj* pix; pix = gluNewQuadric(); gluQuadricDrawStyle(pix, GL_FILL); gluQuadricNormals(pix, GLU_SMOOTH); glPushMatrix(); glTranslatef(dx, dy, (GLdouble) 0.0); gluDisk(pix, (1.0 - pixFills[i][j]) * pixRadius, pixRadius, 20, 1); glPopMatrix(); } void latticeInitalize(){ int i, j, x1, x2, y1, y2; y1 = latStart; y2 = latEnd; for (i = latStart; i <= latEnd; i = i + latSpacing){ drawLineSegment(i, y1, i, y2); } x1 = latStart; x2 = latEnd; for (j = latStart; j <= latEnd; j = j + latSpacing){ drawLineSegment(x1, j, x2, j); } /* Note I am switching from screen pixel to lattice pixel indices! */ for (i = 0; i < latCount; i++){ for (j = 0; j < latCount; j++){ drawLatticePixel(i, j, 0.0, 0.0, 0.0); } } } void drawLattice(){ glClear(GL_COLOR_BUFFER_BIT); /* Clear the display window */ latticeInitalize(); glFlush(); /* Process all OpenGL routines as fast as possible */ } /* This is the standard glu initialization suggested in the textbook */ void init (void) { glClearColor (backRed, backGreen, backBlue, 0.0); glMatrixMode (GL_PROJECTION); gluOrtho2D (0.0, wDimPixels, 0.0, wDimPixels); } //void lineSegment(void){ // glClear(GL_COLOR_BUFFER_BIT); /* Clear the display window */ // drawLineSegment( 0, 0, 300, 200); // glFlush(); /* Process all OpenGL routines as fast as possible */ //} /* Bresenham's Algorithm Here we use bresenham's algorithm to fill lattice pixels between points (px1, py1) and (px2, py2). This is done with two procedures. The first does initialization. The second takes a single step. This is so that we can sequence the individual pixels being filled with the GLUT event timer */ void oct1Step(){ if (bG < 0) { printf("G negative, going East: G = %d\n", bG); /* Go East */ bx = bx + 1; bG = bG + oct2deltaE; } else { /* Go North East */ printf("G positive, going North East: G = %d\n", bG); bx = bx + 1; by = by + 1; bG = bG + oct2deltaNE; } pixFills[bx][by] = 0.9; glutPostRedisplay(); } /* Take the next step for line segments in first octant */ void nextBresenhamOct1(int step){ if (bx < bx2) { oct1Step(); glutTimerFunc(msecDelay,nextBresenhamOct1,step++); } } void beginBresenham(){ /* Setup the ordered endoints, left to right */ if (px2 < px1) { bx1 = px2, by1 = py2; bx2 = px1; by2 = py1; /* Note Swap */ } else { bx1 = px1, by1 = py1; bx2 = px2; by2 = py2; } printf("Beginning Bresenham's Algorithm between (%d, %d) and (%d, %d)\n", bx1, by1, bx2, by2); /* Initialize the deltas and the initial decision variable */ bx = bx1; by = by1; bdx = bx2 - bx1; bdy = by2 - by1; bifa = bdy; bifb = -bdx; if (bdy > 0) { /* Octant 1 or 2 */ if (bdy > bdx) { printf("Drawing in Octant 1 not yet implemented\n"); } else { /* Octant 2 */ bG = (2 * bifa) + bifb; oct2deltaE = (2 * bifa); oct2deltaNE = (2 * bifa) + (2 * bifb); oct1Step(); glutTimerFunc(msecDelay,nextBresenhamOct1,1); } } else { if ( (-bdy) > bdx) { /* Octant 4 */ printf("Drawing in Octant 4 not yet implemented\n"); } else { printf("Drawing in Octant 3 not yet implemented\n"); } } } /* Mouse events It is important to understand that the OpenGL x y plane has the origin in the lower left, while the screen pixel coordinates returned by mouse are row colum coordinates. Hence, the mapping from screen pixels to lattice pixels is different for the horizontal and vertical directions. */ int xLatFromScreenCol(int col){ int a, x; x = col; a = (int) (x / latSpacing); if (a < 0) return 0; if (a >= latCount) return latCount - 1; return a; } int yLatFromScreenRow(int row){ int a, y; y = wDimPixels - row; a = (int) (y / latSpacing); if (a < 0) return 0; if (a >= latCount) return latCount - 1; return a; } /* A left mouse button press selects a pixel. A right mouse button click clears out all the fillled pixels. The left clicks are counted, and every pair of points after clearing the pixels (or at startup) will invoke Bresenham's algorithm to fill the pixels in between. */ void mouse(int button, int state, int col, int row) { int i, j; // fprintf(stdout,"Mouse has been clicked\n"); // fflush(stdout); switch (button) { case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN) { mClickCount++; i = xLatFromScreenCol(col); j = yLatFromScreenRow(row); //printf("Mouse screen coordinates are %d %d map to Lattice position %d %d\n", col, row, i, j); pixFills[i][j] = 0.9; if ((mClickCount % 2) == 0){ px2 = i; py2 = j; beginBresenham(); } else { px1 = i; py1 = j; } glutPostRedisplay(); } break; case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN) { clearPixFills(); glutPostRedisplay(); } break; default: break; } } int main (int argc, char * argv[]) { printf("Begin program to illustrate Bresenham's Algorithm!\n"); glutInit (&argc, argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB ); //GLUT_DEPTH glutInitWindowPosition (startX, startY); glutInitWindowSize (wDimPixels, wDimPixels); glutCreateWindow ("Bresenham's Algorithm"); init(); clearPixFills(); glutDisplayFunc(drawLattice); glutMouseFunc(mouse); glutMainLoop(); return 0; }