/* This program shows a "Moire" pattern, which is generated when two identical patterns are almost but not quite superimposed. In this case, the basic pattern is a set or lines radiating out from a point. One copy of this pattern stays centered in the window, while another copy moves around. When the program starts, the second copy of the pattern is drifiting about randomly. You can stop the drifting by clicking the window with the right moust button. The drifting will resume when you right-click the window again. You can also drag the pattern around by hand, using the right mouse button. This program generates simple animation and event-handling with OpenGL and GLUT. --David Eck */ #include #include #include static GLfloat driftx = 0.0; // x-coordinate of the center of the drifting pattern. static GLfloat drifty = 0.0; // y-coordinate of the center of the drifting pattern. static GLfloat dx, dy; // The amount by which the pattern drifts in each frame. static int dragging = 0; // This is set to 1 while the user is dragging the // pattern with the right mouse button. static int drifting; // This is set to 0 when the user left-clicks the // window, and set bact to 1 when the user clicks // again. It is initialized to 1 in main. Note that // while the user is dragging, this can be either 1 // or zero. However, no actual drift motion occurs // while the user is dragging. static GLdouble start_drag_x; // Coordinates where the user started dragging. static GLdouble start_drag_y; static GLfloat start_driftx; // Values of driftx and drifty when the user started dragging. static GLfloat start_drifty; float randomFloat(void) { // generate a random float, x, that satisfies 0 <= x <= 1 float d; d = abs(rand()) / (float)RAND_MAX; } void init(void) { // Initialize this program; called by main(); glClearColor(0.0, 1.0, 1.0, 0.0); // cyan glShadeModel(GL_FLAT); do { // Set the drift rate of the pattern so it is not too big or too small. dx = randomFloat()*2 - 1; dy = randomFloat()*2 - 1; } while (dx*dx + dy*dy < 0.25); } void putPattern(GLfloat x, GLfloat y) { // Draw the basic pattern with center at (x,y). // This is called twice by display() to make the // two copies of the pattern. float d = 3.141592654 / 50; float theta = 0; glBegin(GL_LINES); for (int i = 0; i < 50; i++) { glVertex2f(x + 200*cos(theta), y + 200*sin(theta)); glVertex2f(x - 200*cos(theta), y - 200*sin(theta)); theta += d; } glEnd(); } void display(void) { // This is set up in main() as the GLUT display callback function. // It will be called whenever the window needs to be redrawn. glClear (GL_COLOR_BUFFER_BIT); glPushMatrix (); glColor3f(1.0, 0.0, 0.0); putPattern(0.0, 0.0); putPattern(driftx, drifty); glPopMatrix (); glutSwapBuffers(); } void drift (void) { // This routine moves the center of the second pattern by the // amount (dx,dy), and sometimes changes (dx,dy). We don't want // the patter to drift too far from the origin, so if it's too far // away, we make sure it is drifting back towards (0,0). About // every 100 steps on average, the drift velocity will change // at random, spontaneously. Note that this routine is set up // in main as the GLUT idle callback function at those times // when the pattern is drifting. At other times, the idle // callback is NULL, so no animation takes place. driftx += dx; drifty += dy; if (driftx < -25) { do { dx = randomFloat(); } while (dx*dx + dy*dy < 0.25); } else if (driftx > 25) { do { dx = - randomFloat(); } while (dx*dx + dy*dy < 0.25); } if (drifty < -25) { do { dy = randomFloat(); } while (dx*dx + dy*dy < 0.25); } else if (drifty > 25) { do { dy = - randomFloat(); } while (dx*dx + dy*dy < 0.25); } if (randomFloat() < 0.01) { // Occasionally, change the drift randomly. do { dx = randomFloat(); dy = randomFloat(); } while (dx*dx + dy*dy < 0.25); } glutPostRedisplay(); // Force a window-redraw event, so display will be called. } void reshape(int w, int h) { // This is set up in main() as the GLUT reshape callback function, which // is called when the window first opens and whenever it changes its // size. Here the window is set up to use horizontal and veritcal // coordinates between -100 and 100. Note that no attempt is made to // preserve the aspect ratio. glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-100.0, 100.0, -100.0, 100.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); } void unproject2D(int x, int y, GLdouble &wx, GLdouble &wy) { // See p. 148, OpenGL Programming Guide. This routine is // called to convert window pixel coordinates (x,y) to // OpenGL coordinates. It is needed because the position // data returned by the two following callback routines // is given in terms of pixels, but we need OpenGL // coordinates if we are going to do anyting with them. GLint viewport[4]; GLdouble mvmatrix[16], projmatrix[16]; GLint realy; GLdouble z; glGetIntegerv(GL_VIEWPORT, viewport); glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); realy = viewport[3] - (GLint)y - 1; gluUnProject((GLdouble)x, (GLdouble)realy, 0.0, mvmatrix, projmatrix, viewport, &wx, &wy, &z); } void drag(int x, int y) { // The is set up as the GLUT drag callback function in main(). It is called // when the mouse is moved while one of its buttons is held down. We only // want to respond if a drag-gesture has been initiated in the following // mouse() routine. We test for this by testing the value of "dragging". // This routine moves the center of the pattern (driftx,drifty), so that // it keeps the same position with respect to the moving mouse that it had // when the mouse button was first pressed. if (!dragging) return; GLdouble wx, wy; unproject2D(x,y,wx,wy); driftx = start_driftx + wx - start_drag_x; drifty = start_drifty + wy - start_drag_y; glutPostRedisplay(); // force a redraw. } void mouse(int button, int state, int x, int y) { // This is set up in main() as the mouse callback function, which is // called when the user clicks the mouse. If the left mouse button // is clicked, toggle the animation by setting the GLUT idle function // and the value of "drifting". If the right mouse button is pressed, // start a drag operation. switch (button) { case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN) { // left mouse button pressed if (drifting) { glutIdleFunc(NULL); drifting = 0; } else { glutIdleFunc(drift); drifting = 1; } } break; case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN) { // right mouse button pressed unproject2D(x, y, start_drag_x, start_drag_y); start_driftx = driftx; start_drifty = drifty; dragging = 1; if (drifting) // turn off drifting animation during drag glutIdleFunc(NULL); } else if (state == GLUT_UP) { // right mouse button released dragging = 0; if (drifting) // if pattern was drifting before drag, restart it. glutIdleFunc(drift); } break; default: break; } } int main(int argc, char** argv) { // Main routine. Set up OpenGL, GLUT, and this program, then // enter the GLUT event loop, which runs until the user closes // the window. glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowPosition(100,100); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(drag); glutIdleFunc(drift); drifting = 1; glutMainLoop(); return 0; }