//C Stuff #include #include #include #include #include //OpenGL Stuff #include #define VPD_MIN 200 #define VPD_DEFAULT 800 #define VPD_MAX 1024 #define ORTHO_SIZE 1.0 #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define TWOPI (2.0 * M_PI) /* * For some godforsaken reason, rand() is returning negative numbers in * FreeBSD and Linux. * * Hence this. */ inline float RANDNUM() { float f = (float)( (float)rand() / (float)(RAND_MAX+1) ); if (f < 0) { return -1.*f; } return f; } enum MENU { MENU_LINES=0, MENU_TRIANGLES, MENU_SPRAY_MORE_POINTS, MENU_RESET, MENU_RANDOMIZE, MENU_ANIMATE, MENU_DRAWSITES, MENU_SITECOLORBLACK, MENU_INCREASE_SITE_SIZE, MENU_DECREASE_SITE_SIZE, MENU_DRAWING, MENU_UNCAP_FPS, MENU_RESET_FPS, MENU_DECREASE_MAX_FPS }; typedef struct SRGB { float r; float g; float b; } RGB; typedef struct SSite { RGB color; float x; float y; float angle; float angularVelocity; float radius; } Site; typedef struct _rgb { unsigned char r,g,b; } ppm_rgb; typedef struct _ppmimage { int sizex,sizey; ppm_rgb *color; } ppmimage; ////////////////// // Global STUFF // ////////////////// #define DEFAULT_NUMPOINTS 64 #define DEFAULT_RANDSITES 32 #define DEFAULT_SITEWIDTH ORTHO_SIZE*.0025 #define DEFAULT_FPS .00825; GLint wid; // GLUT window id GLint vpd = VPD_DEFAULT; // (square) viewport dimensions GLuint cone; //Cone display list GLuint numPoints = DEFAULT_NUMPOINTS; //number of points for the cone fan GLuint randSites = DEFAULT_RANDSITES; //The number of random points to draw from bool animate = false; //whether the world is animated or not bool drawSites = true; //whether or not to draw sites bool siteColorBlack = true; //defines whether sites will be drawn as black or not float siteWidth = DEFAULT_SITEWIDTH; //siteWidth for the rectangle bool useFile = false; //Use the ppm file or not ppmimage* pImage = NULL; bool capFPS = true; float ONE_OVER_FPS = DEFAULT_FPS; //fps cap Site* sites = NULL; /************************************************************/ ppmimage read_ppmimage ( const char *name ) { ppmimage result; int range; FILE *fp = fopen(name,"rb"); if (!fp) { fprintf(stderr,"No file\n"); exit(1); } if (getc(fp)!='P' || getc(fp)!='6') { fprintf(stderr,"Invalid File\n"); exit(1); } fscanf(fp,"%d%d%d\n",&result.sizex,&result.sizey,&range); assert(range==255); // we'll use only rgb range 0...255 here result.color = (ppm_rgb*)malloc(result.sizex*result.sizey*sizeof(ppm_rgb)); fread(result.color,sizeof(ppm_rgb),result.sizex*result.sizey,fp); return result; } ppm_rgb getRGB(float x, float y) { int newX, newY; x+=ORTHO_SIZE; y+=ORTHO_SIZE; x /= ORTHO_SIZE*2.; y /= ORTHO_SIZE*2.; y = 1.-y; //printf("x= %f y= %f\n",x,y); newX = (int)((x*pImage->sizex)-1.); newY = (int)((y*pImage->sizey)-1.); if (newX > pImage->sizex-1) { newX = pImage->sizex-1; } else if (newX < 0) { newX = 0; } if (newY > pImage->sizey-1) { newY = pImage->sizey-1; } else if (newY < 0) { newY = 0; } //printf("X= %d Y= %d sizex=%d\n",newX,newY,pImage->sizex); ppm_rgb p = pImage->color[(newY*pImage->sizex) + newX]; //printf("%d\n",(newY*pImage->sizex) + newX); return pImage->color[(newY*pImage->sizex) + newX]; } /*************************************************************/ /* * Sites will never be NULL when this is called */ inline void reinit_sites() { free(sites); sites = (Site*)malloc(sizeof(Site)*randSites); } /* * */ GLvoid set_random_color(Site* s) { s->color.r = RANDNUM(); s->color.g = RANDNUM(); s->color.b = RANDNUM(); } /* * */ GLvoid set_all_random_color() { for (GLuint i = 0; i < randSites; i++) { set_random_color(sites+i); } } /* * */ GLvoid init_random_sites(int start) { float x, y; for (GLuint i = start; i < randSites; i++) { x = RANDNUM() * ORTHO_SIZE * 2.; y = RANDNUM() * ORTHO_SIZE * 2.; x -= ORTHO_SIZE; y -= ORTHO_SIZE; sites[i].x = x; sites[i].y = y; sites[i].radius = sqrt(pow(x,2)+pow(y,2)); /* Add by quadrant for ease */ if ( x >= 0. && y >= 0.) { //Quadrant 1 sites[i].angle = atan(y/x); } else if ( x < 0. && y >= 0. ) { //Quad 2 sites[i].angle = atan(y/-x) + (M_PI/2.); } else if ( x < 0. && y < 0. ) { //Quad 3 sites[i].angle = atan(-y/-x) + M_PI; } else { //Quad 4 sites[i].angle = atan(-y/x) + (M_PI) + (M_PI/2.); } set_random_color(sites+i); sites[i].angularVelocity = RANDNUM() * (4.) * ((rand() % 2) ? -1. : 1.); //convert to radians sites[i].angularVelocity /= (180./M_PI); } } GLvoid init_random_sites() { init_random_sites(0); } /** * This performs window resizing, like project 2 */ GLvoid window_resize(GLint vpw, GLint vph) { glutSetWindow(wid); /* maintain a square viewport, not too small, not too big */ if( vpw < vph ) vpd = vph; else vpd = vpw; if( vpd < VPD_MIN ) vpd = VPD_MIN; if( vpd > VPD_MAX ) vpd = VPD_MAX; glViewport(0, 0, vpd, vpd); glutReshapeWindow(vpd, vpd); glutPostRedisplay(); } /** * Window menu */ GLvoid menu ( int value ) { switch (value) { case MENU_LINES: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glutPostRedisplay(); break; case MENU_TRIANGLES: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glutPostRedisplay(); break; case MENU_SPRAY_MORE_POINTS: randSites *= 2; sites = (Site*)realloc(sites,sizeof(Site)*randSites); init_random_sites(randSites/2); glutPostRedisplay(); break; case MENU_RESET: animate = false; siteColorBlack = true; drawSites = true; useFile = false; siteWidth = DEFAULT_SITEWIDTH; //Avoid constantly rerandomizing if (randSites == DEFAULT_RANDSITES) { glutPostRedisplay(); break; } case MENU_RANDOMIZE: useFile = false; animate = false; siteColorBlack = true; drawSites = true; randSites = DEFAULT_RANDSITES; reinit_sites(); init_random_sites(); glutPostRedisplay(); break; case MENU_ANIMATE: animate ? animate = false : animate = true; glutPostRedisplay(); break; case MENU_DRAWSITES: drawSites ? drawSites = false : drawSites = true; glutPostRedisplay(); break; case MENU_SITECOLORBLACK: siteColorBlack ? siteColorBlack = false : siteColorBlack = true; glutPostRedisplay(); break; case MENU_INCREASE_SITE_SIZE: siteWidth *= 2; glutPostRedisplay(); break; case MENU_DECREASE_SITE_SIZE: siteWidth /= 2; glutPostRedisplay(); break; case MENU_DRAWING: useFile ? useFile = false : useFile = true; glutPostRedisplay(); break; case MENU_UNCAP_FPS: capFPS ? capFPS = false : capFPS = true; break; case MENU_RESET_FPS: ONE_OVER_FPS = DEFAULT_FPS; break; case MENU_DECREASE_MAX_FPS: ONE_OVER_FPS *= 2.; break; default: break; } } /** * Keyboard input callback */ GLvoid keyboard_event(GLubyte key, GLint x, GLint y) { switch(key) { case 27: /* ESC */ exit(0); case 'l': menu(MENU_LINES); break; case 'f': menu(MENU_TRIANGLES); break; case 's': menu(MENU_SPRAY_MORE_POINTS); break; case 'r': menu(MENU_RESET); break; case 'e': menu(MENU_RANDOMIZE); break; case 'a': menu(MENU_ANIMATE); break; case 'd': menu(MENU_DRAWSITES); break; case 'i': menu(MENU_SITECOLORBLACK); break; case '+': menu(MENU_INCREASE_SITE_SIZE); break; case '-': menu(MENU_DECREASE_SITE_SIZE); break; case 't': menu(MENU_DRAWING); break; case 'u': menu(MENU_UNCAP_FPS); break; case 'j': menu(MENU_RESET_FPS); break; case 'k': menu(MENU_DECREASE_MAX_FPS); break; default: break; } } /** * Handle mouse drag motion */ GLvoid button_motion(GLint mx, GLint my) { //printf("Dragging %d %d\n",mx,my); return; } /** * Handle mouse movement */ GLvoid passive_motion(GLint mx, GLint my) { //printf("Someone is moving above me %d %d\n",mx,my); return; } /** * Redraw the view */ GLvoid redraw(GLvoid) { clock_t frame = clock(); /* set the projection matrix */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-ORTHO_SIZE,ORTHO_SIZE,-ORTHO_SIZE,ORTHO_SIZE,15.0,25.0); //gluPerspective(20.0,1.0,15.0,25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0,0,-20.0); /* ensure we're drawing to the correct GLUT window */ glutSetWindow(wid); /* clear the color buffers */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ppm_rgb temp; /* Draw random points */ for (GLuint i = 0; i < randSites; i++) { glPushMatrix(); if (animate) { sites[i].angle += sites[i].angularVelocity; } if (sites[i].angle >= 2.*M_PI) { sites[i].angle -= 2.*M_PI; } //glRotatef(sites[i].angle,0,0,1); /* * x = r*cos(angle) * y = r*sin(angle) */ sites[i].x = sites[i].radius * cos(sites[i].angle); sites[i].y = sites[i].radius * sin(sites[i].angle); glTranslatef(sites[i].x, sites[i].y, 0); if (useFile && pImage) { temp = getRGB(sites[i].x,sites[i].y); glColor3f(temp.r / 255., temp.g / 255., temp.b / 255.); } else { glColor3f(sites[i].color.r, sites[i].color.g, sites[i].color.b); } glCallList(cone); if (drawSites) { if (siteColorBlack) { glColor3f(0,0,0); } else { glColor3f(1-sites[i].color.r, 1-sites[i].color.g, 1-sites[i].color.b); } glRectf(-siteWidth,-siteWidth,siteWidth,siteWidth); } glPopMatrix(); } /* flush the pipeline */ glFlush(); /* look at our handiwork */ glutSwapBuffers(); if (animate && !capFPS) { glutPostRedisplay(); }else if (animate) { float temp = CLOCKS_PER_SEC*ONE_OVER_FPS; while (clock() - frame < temp); glutPostRedisplay(); } } /** * Mouse button pressed */ GLvoid mouse_button(GLint btn, GLint state, GLint mx, GLint my) { return; } /** * This handles the glut initialization */ GLint init_glut(GLint *argc, char **argv) { GLint id; glutInit(argc,argv); /* size and placement hints to the window system */ glutInitWindowSize(vpd, vpd); glutInitWindowPosition(10,10); /* double buffered, RGB color mode */ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); /* create a GLUT window (not drawn until glutMainLoop() is entered) */ id = glutCreateWindow("cs4451 project 3: Voronoi Diagram"); /* register callbacks */ /* window size changes */ glutReshapeFunc(window_resize); /* keypress handling when the current window has input focus */ glutKeyboardFunc(keyboard_event); /* mouse event handling */ glutMouseFunc(mouse_button); /* button press/release */ glutMotionFunc(button_motion); /* mouse motion w/ button down */ glutPassiveMotionFunc(passive_motion); /* mouse motion with button up */ /* window obscured/revealed event handler */ glutVisibilityFunc(NULL); /* handling of keyboard SHIFT, ALT, CTRL keys */ glutSpecialFunc(NULL); /* what to do when mouse cursor enters/exits the current window */ glutEntryFunc(NULL); /* what to do on each display loop iteration */ glutDisplayFunc(redraw); /* Create the menu */ GLint viewMenu = glutCreateMenu(menu); glutAddMenuEntry("Lines Only (l)",MENU_LINES); glutAddMenuEntry("Full View (f)",MENU_TRIANGLES); GLint siteMenu = glutCreateMenu(menu); glutAddMenuEntry("Show/Hide Sites (d)",MENU_DRAWSITES); glutAddMenuEntry("Toggle inverse site coloring (i)",MENU_SITECOLORBLACK); glutAddMenuEntry("Increase site size (+)",MENU_INCREASE_SITE_SIZE); glutAddMenuEntry("Decrease site size (-)",MENU_DECREASE_SITE_SIZE); GLint fpsMenu = glutCreateMenu(menu); glutAddMenuEntry("Increase Max FPS (j)",MENU_RESET_FPS); glutAddMenuEntry("Decrease Max FPS (k)",MENU_DECREASE_MAX_FPS); glutAddMenuEntry("Uncap FPS (u)",MENU_UNCAP_FPS); /*GLint menuID = */ glutCreateMenu(menu); glutAddSubMenu("View Options",viewMenu); glutAddSubMenu("Site Options",siteMenu); glutAddSubMenu("Framerate Options",fpsMenu); glutAddMenuEntry("Spray More Points (s)",MENU_SPRAY_MORE_POINTS); glutAddMenuEntry("Move Points (a)",MENU_ANIMATE); glutAddMenuEntry("Toggle Coloring Method (t)",MENU_DRAWING); glutAddMenuEntry("Reset (r)",MENU_RESET); glutAddMenuEntry("Reset-Randomize (e)",MENU_RANDOMIZE); glutAttachMenu(GLUT_RIGHT_BUTTON); return id; } /** * OpenGL initializations */ GLvoid init_opengl(GLvoid) { /* clear to BLACK */ glClearColor(0.0, 0.0, 0.0, 1.0); /* Enable depth test */ glEnable(GL_DEPTH_TEST); } /////////////////////////// // Voronoi Diagram stuff // /////////////////////////// GLuint init_cone ( ) { GLuint sceneList = glGenLists(1); float temp; glNewList(sceneList,GL_COMPILE); glBegin(GL_TRIANGLE_FAN); //Initialize the first vertex glVertex3f(0,0,0); for (GLuint k = 0; k < numPoints; k++) { temp = (TWOPI*k) / (float)numPoints; glVertex4f(cos(temp),sin(temp),-1,0); } glVertex4f(cos(0.),sin(0.),-1,0); glEnd(); glEndList(); return sceneList; } int main(int argc, char* argv[]) { ppmimage image; srand(time(NULL)); if (argc >= 2) { image = read_ppmimage(argv[1]); pImage = ℑ } wid = init_glut(&argc,argv); init_opengl(); cone = init_cone(); sites = (Site*)malloc(sizeof(Site)*randSites); init_random_sites(); glutMainLoop(); return 0; }