//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) //This will store each pixel's final spot typedef struct pixelMap { int x; int y; } Pixel; void doFancyAttractorStuff(); /* * 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_GRABSCREENCAP }; typedef struct SRGB { float r; float g; float b; } RGB; typedef struct SSite { RGB color; float x; float y; } 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 0 #define DEFAULT_SITEWIDTH ORTHO_SIZE*.0025 #define SITE_PRECISION .009 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; GLfloat *screenCap = NULL; //bool capFPS = true; //float ONE_OVER_FPS = DEFAULT_FPS; //fps cap Pixel* current = NULL; GLubyte* newScreen = NULL; 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; set_random_color(sites+i); } } 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; if (newScreen != NULL) { free(newScreen); newScreen = NULL; } 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;*/ case MENU_GRABSCREENCAP: drawSites = false; glutPostRedisplay(); doFancyAttractorStuff(); //drawSites = true; //glutPostRedisplay(); 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) { if (newScreen != NULL) { glutSetWindow(wid); //glPushMatrix(); glRasterPos2i(-1,-1); glColor3f(1.0,1.0,1.0); glDrawPixels(vpd,vpd,GL_RGB,GL_UNSIGNED_BYTE,newScreen); glFlush(); glutSwapBuffers(); //glPopMatrix(); return; } 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(); 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(); } /*if (current != NULL) { float x = (current->x / vpd) * ORTHO_SIZE * 2.; float y = (current->y / vpd) * ORTHO_SIZE * 2.; x -= ORTHO_SIZE; y -= ORTHO_SIZE; y = ORTHO_SIZE*2. - y; glPushMatrix(); glTranslatef(x,y,0); glRectf(-siteWidth,-siteWidth,siteWidth,siteWidth); glPopMatrix(); }*/ /* flush the pipeline */ glFlush(); /* look at our handiwork */ glutSwapBuffers(); printf("Finished redisplay"); } /** * Mouse button pressed */ GLvoid mouse_button(GLint btn, GLint state, GLint mx, GLint my) { if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { randSites++; sites = (Site*)realloc(sites,sizeof(Site)*randSites); init_random_sites(randSites-1); sites[randSites-1].x = ((mx/((float)vpd)) * ORTHO_SIZE * 2.) - ORTHO_SIZE; sites[randSites-1].y = -1.*(((my/((float)vpd)) * ORTHO_SIZE * 2.)-ORTHO_SIZE); printf("Added Site at: %d %d w/ %f %f %f\n",mx,my, sites[randSites-1].color.r,sites[randSites-1].color.g, sites[randSites-1].color.b); glutPostRedisplay(); } 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("Grab Screencap",MENU_GRABSCREENCAP); 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(); randSites = 0; sites = (Site*)malloc(sizeof(Site)*randSites); init_random_sites(); glutMainLoop(); return 0; } inline bool movePixel(Pixel* curr, Pixel* center) { float distance1, distance2, distance3, distance4; float xP1, yP1; bool tx, ty; //x++ if((curr->x< vpd-1) && *((screenCap+(curr->y*vpd*3))+(curr->x*3)) == *((screenCap+(curr->y*vpd*3))+((curr->x+1)*3))) { distance1 = sqrt(pow(curr->x+1,2)+pow(curr->y,2)); } else { distance1 = 0; } //x-- if((curr->x > 1) && *((screenCap+(curr->y*vpd*3))+(curr->x*3)) == *((screenCap+(curr->y*vpd*3))+((curr->x-1)*3))) { distance2 = sqrt(pow(curr->x-1,2)+pow(curr->y,2)); } else { distance2 = 0; } tx = distance1 > distance2; xP1 = tx ? distance1 : distance2; //y++ if((curr->y < vpd-1) && *((screenCap+(curr->y*vpd*3))+(curr->x*3)) == *((screenCap+((curr->y+1)*vpd*3))+(curr->x*3))) { distance3 = sqrt(pow(curr->x,2) +pow(curr->y+1,2)); } else { distance3 = 0; } //y-- if((curr->y > 1) && *((screenCap+(curr->y*vpd*3))+(curr->x*3)) == *((screenCap+((curr->y-1)*vpd*3))+(curr->x*3))) { distance4 = sqrt(pow(curr->x,2) +pow(curr->y-1,2)); } else {distance4 = 0;} ty = distance3 > distance4; yP1 = ty ? distance3 : distance4; if (yP1 == xP1 == 0) { return true; } else if (xP1 > yP1) { tx ? curr->x+=1 : curr->x-=1; return false; } else if (xP1 > yP1) { ty ? curr->y+=1 : curr->y-=1; return false; } return false; } void doFancyAttractorStuff() { //This will give us every pixel Pixel *pixelMap = (Pixel*)malloc(vpd*vpd*sizeof(Pixel)); Pixel *attractors = NULL; int numAttractors = 0; //Allocate space for the current screen screenCap = (GLfloat*)realloc(screenCap,vpd*vpd*3*sizeof(GLfloat)); newScreen = (GLubyte*)realloc(newScreen,vpd*vpd*3*sizeof(GLubyte)); //Quake2's Source code came in useful, figured out what function to use ;) glReadPixels (0, 0, vpd, vpd, GL_RGB, GL_FLOAT, screenCap ); Pixel *curr; //float distance; Pixel center; RGB color; int lastX, lastY; for (int j = 0; j < vpd; j++) { for (int i = 0; i < vpd; i++) { curr = (pixelMap+(j*vpd))+i; curr->x = i; curr->y = j; color.r = *((screenCap+(j*vpd*3))+(i*3)); color.g = *((screenCap+(j*vpd*3))+(i*3)+1); color.b = *((screenCap+(j*vpd*3))+(i*3)+2); for (GLuint k = 0; k < randSites; k++) { if ( sites[k].color.r - color.r <= SITE_PRECISION && sites[k].color.g - color.g <= SITE_PRECISION && sites[k].color.b - color.b <= SITE_PRECISION) { center.x = .5*vpd*(sites[k].x + ORTHO_SIZE); center.y = .5*vpd*((2.*ORTHO_SIZE)-(sites[k].y + ORTHO_SIZE)); break; } } //We now have the point we want to run like hell from lastX = curr->x; lastY = curr->y; movePixel(curr,¢er); int nX, nY; nX = .5*vpd*(curr->x + ORTHO_SIZE); nY = .5*vpd*((2.*ORTHO_SIZE)-(curr->y + ORTHO_SIZE)); if (*((screenCap+(nY*vpd*3))+(nX*3)) != color.r || *((screenCap+(nY*vpd*3))+(nX*3)+1) != color.g || *((screenCap+(nY*vpd*3))+(nX*3)+2) != color.b) { color.r = *((screenCap+(nY*vpd*3))+(nX*3)); color.g = *((screenCap+(nY*vpd*3))+(nX*3)+1); color.b = *((screenCap+(nY*vpd*3))+(nX*3)+2); for (GLuint k = 0; k < randSites; k++) { if ( sites[k].color.r - color.r <= SITE_PRECISION && sites[k].color.g - color.g <= SITE_PRECISION && sites[k].color.b - color.b <= SITE_PRECISION) { center.x = .5*vpd*(sites[k].x + ORTHO_SIZE); center.y = .5*vpd*((2.*ORTHO_SIZE)-(sites[k].y + ORTHO_SIZE)); break; } } } while (true) { //glutPostRedisplay(); if (curr->x < 0 || curr->x >= vpd || curr->y < 0 || curr->y >= vpd) { //printf("Fell Outside\n"); curr->x = curr->y = -1; *((newScreen+(j*vpd*3))+(i*3)) = 255; *((newScreen+(j*vpd*3))+(i*3)+1) = 255; *((newScreen+(j*vpd*3))+(i*3)+2) = 255; break; } else if ( rand() % 3 ) { lastX = curr->x; lastY = curr->y; } movePixel(curr,¢er); nX = .5*vpd*(curr->x + ORTHO_SIZE); nY = .5*vpd*((2.*ORTHO_SIZE)-(curr->y + ORTHO_SIZE)); if (*((screenCap+(nY*vpd*3))+(nX*3)) != color.r || *((screenCap+(nY*vpd*3))+(nX*3)+1) != color.g || *((screenCap+(nY*vpd*3))+(nX*3)+2) != color.b) { color.r = *((screenCap+(nY*vpd*3))+(nX*3)); color.g = *((screenCap+(nY*vpd*3))+(nX*3)+1); color.b = *((screenCap+(nY*vpd*3))+(nX*3)+2); for (GLuint k = 0; k < randSites; k++) { if ( sites[k].color.r - color.r <= SITE_PRECISION && sites[k].color.g - color.g <= SITE_PRECISION && sites[k].color.b - color.b <= SITE_PRECISION) { center.x = .5*vpd*(sites[k].x + ORTHO_SIZE); center.y = .5*vpd*((2.*ORTHO_SIZE)-(sites[k].y + ORTHO_SIZE)); break; } } } if (abs(curr->x - lastX) + abs(curr->y - lastY) == 0) { printf("%d %d At attractor: %3d %3d\n",i,j,curr->x,curr->y); *((newScreen+(j*vpd*3))+(i*3)) = i+j; *((newScreen+(j*vpd*3))+(i*3)+1) = i+j; *((newScreen+(j*vpd*3))+(i*3)+2) = i+j; /*int k; for (k = 0; k < numAttractors; k++) { if (abs(attractors[i].x - curr->x) + abs(attractors[i].y - curr->y) < 8) { printf("At attractor %d\n",k); break; } } if (k == numAttractors) { printf("New Attractor %d\n",numAttractors); numAttractors++; attractors = (Pixel*)realloc(attractors,sizeof(Pixel)*numAttractors); attractors[k].x = curr->x; attractors[k].y = curr->y; }*/ break; } } } //printf("col %d: %f %f %f\n",j,color.r,color.g,color.b); } }