/* This program allows the user to draw straight red lines in a right drawing area. A "menu strip" to the right of the drawing area contains commands "Clear" and "Undo" that the user can click on. If the user holds down the shift key, the line is constrained to be vertical or horizontal. Double-buffering is used to implement a "rubber-band" effect as the user drags the mouse during a drawing operation. */ #include #include #include #define MAX_LINES 500 static int lineCt = 0; // Set up arrays to hold data for the lines. static float x1[MAX_LINES], y1[MAX_LINES], x2[MAX_LINES], y2[MAX_LINES]; static int width, height; // Current width and height of window, in pixels. void init() { // Initialize some OpenGL stuff. glClearColor(1.0,1.0,1.0,1.0); } void putString(const char *str, float x, float y) { // Draw the string starting at the point (x,y), // scaled to fit the menu strip! int i, len; glPushMatrix(); glTranslatef(x,y,0); glScalef(0.15,0.15,0.15); len = strlen(str); for (i = 0; i < len; i++) { glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN,str[i]); } glPopMatrix(); } void putLine(float x1, float y1, float x2, float y2) { // Draw one line. glBegin(GL_LINES); glVertex2f(x1,y1); glVertex2f(x2,y2); glEnd(); } void reshape(int w, int h) { // Reshape callback function -- just record the height and width. width = w; height = h; } void display(void) { // Display callback. Redraw the drawing area and the menu area // completely in the back buffer, then call glutSwapBuffers(). glClear(GL_COLOR_BUFFER_BIT); // First, set up a viewport and projection for the menu strip, // a 100-pixel strip at the right edge of the window. Use // pixel coordinates with (0,0) at the lower left and // (100,height) at the top right of the viewport. glViewport(width-100, 0, 100, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,100,0,height); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); glClear(GL_COLOR_BUFFER_BIT); // Draw the menu strip. glColor3f(0.0,1.0,0.0); glRectf(0,0,100,height); glColor3f(0.0,0.0,0.0); putLine(0,0,0,height); putLine(0,height-50,100,height-50); putLine(0,height-100,100,height-100); putString("Clear",10,height-30); putString("Undo",10,height-80); // Next, set up a viewport and projection for the drawing area, // which occupies the whole window except for the menu strip. // The projection assigns (0,0) to the lower left of the // drawing area and (1,1) to the upper right. glViewport(0, 0, width - 100, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,1,0,1); glMatrixMode(GL_MODELVIEW); // Now, redraw all the lines, if any, in red. glColor3f(1.0,0.0,0.0); if (lineCt > 0) { glBegin(GL_LINES); for (int i = 0; i < lineCt; i++) { glVertex2f(x1[i],y1[i]); glVertex2f(x2[i],y2[i]); } glEnd(); } // Finally, copy the back buffer to the window. glutSwapBuffers(); } // end display() //-------------------------------------------------------------------- // This section lets the user draw lines by clicking and dragging // with the left mouse button. static int dragging = 0; // True if drawing operation is in progress. static int drawStraightLine; // True if SHIFT was down when // drawing operation was begun. inline void myViewportToWorld(int vx, int vy, float &wx, float &wy) { // This routine will convert pixel coordinates (vx,vy) to the // coordinate system used in the drawing portion of the window. // The converted coordinates are returned as (wx,wy). wx = (float)vx / (width-100); wy = (float)(height - vy) / height; } inline float abs(float x) { // Absolute value function. return (x > 0)? x : -x; } void mouse(int button, int state, int vx, int vy) { // Mouse callback function. Only the left mouse button // has any effect. If the user clicks in the drawing area, // a new line is created and a dragging operation is begun // during which the user specifies the second endpoint of // that line. If the user clicks the "Clear" or "Undo" // command in the menu strip, then the appropriate command // is executed. When the user releases the mouse, the // 2-nd endpoint of the line is finalized. (However, if the // mouse ends at the same point it began, then the new line // is removed from the data.) float x,y; // Mouse position in window coordinates for drawing area. if (button == GLUT_LEFT_BUTTON) { if (state == GLUT_DOWN) { // left mouse button pressed if (vx < width-100) { myViewportToWorld(vx, vy, x, y); dragging = 1; // Start a draw operation. drawStraightLine = (glutGetModifiers() & GLUT_ACTIVE_SHIFT); x1[lineCt] = x; // Create new line, initially from (x,y) to (x,y) y1[lineCt] = y; x2[lineCt] = x; // (This 2-nd coord will change as the y2[lineCt] = y; // user moves the mouse.) lineCt++; } else if (vy < 50) { // Clear command -- remove all lines and redraw. lineCt = 0; glutPostRedisplay(); } else if (vy < 100) { // Undo command -- remove one line and redraw. if (lineCt > 0) { lineCt--; glutPostRedisplay(); } } } else if (dragging) { myViewportToWorld(vx, vy, x, y); dragging = 0; if (x == x1[lineCt] && y == y1[lineCt]) // Mouse at starting point. lineCt--; // Remove line from data. else { x2[lineCt] = x; // Final value of 2-nd endpoint. y2[lineCt] = y; } glutPostRedisplay(); } } } // end mouse() void drag(int vx, int vy) { // Motion callback function, which is called when the user // drags the mouse. If a draw operation is actually in // progress, the line in position (lineCt-1) in the // data arrays is modified to reflect the current position // of the mouse. If the shift key was down then the line is // constrained to be vertical or horizontal; the longer of // these two possiblities is the one that is chosen. float x,y; // Mouse position in window coordinates. if (!dragging) return; myViewportToWorld(vx, vy, x, y); if (!drawStraightLine) { // if shift was not down, use a line to current point x2[lineCt-1] = x; y2[lineCt-1] = y; } else if (abs(x1[lineCt-1] - x) > abs(y1[lineCt-1] - y)) { // if horizontal line is longer, use horizontal line x2[lineCt-1] = x; y2[lineCt-1] = y1[lineCt-1]; } else { // if vertical line is longer, use vertical line x2[lineCt-1] = x1[lineCt-1]; y2[lineCt-1] = y; } glutPostRedisplay(); // Redraw image completely. } // end drag(); //------------------------------------------------------------- int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(500,400); glutInitWindowPosition(100,100); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(drag); glutMainLoop(); return 0; }