// Graham Donovan // graham37@u // CSE 142 Homework #4 // Section BH, TA: Chun Yu // Rotating 3d-ness // This program is quite different from the // sample program, so here's a brief explanation. // I've kept some of the central code - the event // loop, button draw functions, and periodic update, // but all of the drawing is very different. There // is no longer any scenery, but instead a 3d-ish // figure that can be changed in a variety of ways. // The user can change color, size, and rotation. // The theta and phi buttons really change // theta prime and phi prime, but for simplicity // I haven't labled the buttons this way. // So then, the basic implementation of the program // is based on spherical coordinate tracking of a // single point, so theta and phi aren't arrays but // only track 1 point. Theta and phi change with // the periodic update at a rate of dtheta and dphi, // which the use can change with the buttons. The // angles are then passed to calc_3d, which does // something a little bit different depending on // the current type. The first transform is simply // spherical -> rectangular conversion, and the // others just do various things that look neat - // a full explanation is rather lengthy. calc_3d // puts values into x_3[], y_3[], and z_3[], which // are 3d coordinates for each point. Then flatten // converts these into 2d coordinates for display, // and draw_cube draws a bunch of lines to connect // the verticies. #include "gp142.h" #include #include #define PI 3.14159 #define MENU_TOP (GP142_YMAX - 5) #define MENU_LEFT (-GP142_XMAX + 5) #define BUTTON_HEIGHT 25 #define BUTTON_WIDTH 100 #define NUM_BUTTONS 12 #define TRUE 1 #define FALSE 0 #define BUTTON_CLICK 1 #define OTHER_CLICK 0 #define FILL 0 // determines whether user clicked button or not int classify_mouse_click(int mouse_x, int mouse_y); // handle a button click. all the pointers allow the user to change // program values with a button. void handle_button(int x, int y, double *dtheta, double *dphi, double *alpha, int *quit, int *length, int *color, int *type); // just in case void something_is_wrong (void); // draw everything. values are displayed on buttons void draw_buttons(double theta, double phi, double alpha); // change the angles (called from periodic update) void rotate(double* theta, double* phi, double dtheta, double dphi); // calc the 3d points void calc_3d(int x_3[], int y_3[], int z_3[], double theta, double phi, double alpha, int length, int type); // flatten to 2d void flatten(int x_2[], int y_2[], int x_3[], int y_3[], int z_3[]); // draw it! void draw_cube(int x_2[], int y_2[], int x_3[], double theta, double dtheta, int color, int y_3[], int z_3[]); // background void draw_background(); // begin main int main(void){ // declare variables int event; int mouse_x, mouse_y; int quit; char key_pressed; // unused int click_kind; double dtheta = 1.0, dphi = 0.0, dalpha = 0.0; // rate of spin double theta = 0.0, phi = 1; // spin position int x_3[8], y_3[8], z_3[8]; // 3d coords int x_2[8], y_2[8]; // 2d coords int color = WHITE; // default color int length = 50; double alpha = 75.0; int type = 1; // start with a fairly normal type // start up the graphics package GP142_open(); GP142_logging(LOG_OFF); GP142_clear(); GP142_animate(ANI_RUN); // draw everything the first time draw_background(); draw_buttons(dtheta, dphi, alpha); calc_3d(x_3, y_3, z_3, theta, phi, alpha, length, type); flatten(x_2, y_2, x_3, y_3, z_3); draw_cube(x_2, y_2, x_3, theta, dtheta, color, y_3, z_3); // begin event loop quit = FALSE; while (!quit) { /* get next event */ event = GP142_await_event(&mouse_x, &mouse_y, &key_pressed); /* perform appropriate action depending on kind of event */ switch (event) { case GP142_QUIT: /* Quit selected */ quit = TRUE; break; case GP142_KBD: // no resonse to keyboard break; case GP142_MOUSE: /* Mouse click. Determine if in a menu button */ /* or elsewhere and process accordingly. */ click_kind = classify_mouse_click(mouse_x, mouse_y); if (click_kind == BUTTON_CLICK) { handle_button(mouse_x, mouse_y, &dtheta, &dphi, &alpha, &quit, &length, &color, &type); } break; case GP142_PERIODIC: // move everything around rotate(&theta, &phi, dtheta, dphi); break; default: /* unknown event */ break; } // refresh everything after the rotation draw_background(); draw_buttons(dtheta, dphi, alpha); calc_3d(x_3, y_3, z_3, theta, phi, alpha, length, type); flatten(x_2, y_2, x_3, y_3, z_3); draw_cube(x_2, y_2, x_3, theta, dtheta, color, y_3, z_3); /*end switch */ } GP142_close(); return(0); } // ******************************************* // draw all the buttons void draw_buttons(double dtheta, double dphi, double alpha) { int i; int text_offset = MENU_LEFT+10; /* offset of button text in button */ int font_size = 12; /* size of button text */ /* draw rectangles for each button */ for(i = 0; i < NUM_BUTTONS; i++) { GP142_rectangleXY(WHITE, MENU_LEFT, MENU_TOP - i * BUTTON_HEIGHT, MENU_LEFT + BUTTON_WIDTH, MENU_TOP - (i + 1) * BUTTON_HEIGHT, 1); } /* display button text */ i = MENU_TOP - (BUTTON_HEIGHT/2) - (font_size/2); GP142_printfXY(WHITE, text_offset, i, font_size, "Increase Theta"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Decrease Theta"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Increase Phi"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Decrease Phi"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Increase size"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Decrease size"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Change color"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Change type"); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Theta: %.2f", dtheta); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Phi: %.2f", dphi); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "size: %.2f", alpha); i -= BUTTON_HEIGHT; GP142_printfXY(WHITE, text_offset, i, font_size, "Quit"); } // ************************************************ // figure out where the user clicked int classify_mouse_click(int mouse_x, int mouse_y) { if ((mouse_x > MENU_LEFT) && (mouse_x < MENU_LEFT + BUTTON_WIDTH) && (mouse_y < MENU_TOP) && (mouse_y > (MENU_TOP - NUM_BUTTONS * BUTTON_HEIGHT))) return BUTTON_CLICK; else return OTHER_CLICK; } // ************************************************** /* Process button click at location x, y. React appropriately: */ /* i.e. change colors, tree tops, etc. if requested. */ void handle_button(int x, int y, double *dtheta, double *dphi, double *alpha, int *quit, int *length, int *color, int *type) { int buttonNum; /* number of selected button */ /* Complain if x coordinate couldn't be inside a button */ if (x < MENU_LEFT || x > MENU_LEFT + BUTTON_WIDTH) { something_is_wrong(); return; } /* Convert y coordinate to button number from top. */ buttonNum = (MENU_TOP - y)/BUTTON_HEIGHT; switch(buttonNum) { case 0: // increase theta *dtheta = *dtheta + 1.0; break; case 1: // decrease theta *dtheta = *dtheta - 1.0; break; case 2: // increase phi *dphi = *dphi + 1.0; break; case 3: // decrease phi *dphi = *dphi - 1.0; break; case 4: // increase alpha *alpha = *alpha + 5.0; break; case 5: // decrease alpha *alpha = *alpha - 5.0; break; case 6: // change color *color = ++*color % 16 + 1; break; case 7: // change type *type = ++*type % 5; break; case 8: // display only break; case 9: // display only break; case 10: // display only break; case 11: // quit *quit = TRUE; break; default: /*Bad news if this is ever reached! */ GP142_animate(ANI_HALT); something_is_wrong(); break; } } // ************************************************** /* Display error message if internal error is detected. */ void something_is_wrong (void) { GP142_printfXY (RED, 0, 0, 16, "Program error!"); } // *************************************************** // move everything along. /10 to keep it from spinning // too quickly. void rotate(double* theta, double* phi, double dtheta, double dphi){ *theta = *theta + dtheta/10; *phi = *phi + dphi/10; } // *************************************************** // just 1 black rectangle void draw_background(){ GP142_rectangleXY(BLACK, -GP142_XMAX, -GP142_YMAX, GP142_XMAX, GP142_YMAX, FILL); } // *************************************************** // calculate the vertices in 3d based on theta and phi // all very similar. void calc_3d(int x_3[], int y_3[], int z_3[], double theta, double phi, double alpha, int length, int type){ int n; double i, j = 1; switch(type){ case 0: for(n = 0; n < 8; n++){ if(n == 0 || n == 1 || n == 2 || n == 3) i = 0; if(n == 4 || n == 5 || n == 6 || n == 7) i = (PI / 2); if(n == 0 || n == 4 || n == 1 || n == 5) j = -1; else j = 1; x_3[n] = (int)(sin(phi - i) * cos(theta + n * PI / 2) * alpha); y_3[n] = (int)(alpha * cos(phi - i)); z_3[n] = (int)(sin(phi - i) * sin(theta + n * PI / 2) * alpha); } break; case 1: length = (int)alpha; for(n = 0; n < 8; n++){ if(n == 0 || n == 1 || n == 2 || n == 3) i = 0; if(n == 4 || n == 5 || n == 6 || n == 7) i = (PI / 2); x_3[n] = (int)(sin(phi - i) * length); y_3[n] = (int)(cos(theta + n * PI/2) * length); z_3[n] = (int)((sin(theta + n * PI/2))* length); } break; case 2: for(n = 0; n < 8; n++){ if(n == 0 || n == 1 || n == 2 || n == 3) i = 0; if(n == 4 || n == 5 || n == 6 || n == 7) i = (PI / 2); if(n == 0 || n == 4 || n == 1 || n == 5) j = -1; else j = 1; x_3[n] = (int)(sin(phi + n * PI/2) * cos(theta + n * PI / 2) * alpha); y_3[n] = (int)(sin(phi + n * PI/2) * alpha); z_3[n] = (int)(alpha * cos(phi - i)); } break; case 3: for(n = 0; n < 8; n++){ if(n == 0 || n == 1 || n == 2 || n == 3) i = 0; if(n == 4 || n == 5 || n == 6 || n == 7) i = (PI / 2); if(n == 0 || n == 4 || n == 1 || n == 5) j = -1; else j = 1; x_3[n] = (int)(sin(phi - i) * cos(theta + n * PI / 2) * alpha); y_3[n] = (int)(cos(phi - i) * sin(theta + n * PI / 2) * alpha); z_3[n] = (int)(alpha * cos(phi - i)); } break; case 4: for(n = 0; n < 8; n++){ if(n == 0 || n == 1 || n == 2 || n == 3) i = 0; if(n == 4 || n == 5 || n == 6 || n == 7) i = (PI / 2); if(n == 0 || n == 4 || n == 1 || n == 5) j = -1; else j = 1; x_3[n] = (int)(j * sin(phi - i) * cos(theta + n * PI / 2) * alpha); y_3[n] = (int)(sin(phi - i) * sin(theta + n * PI / 2) * alpha); z_3[n] = (int)(alpha * cos(phi - i)); } break; default: for(n = 0; n < 8; n++){ if(n == 0 || n == 1 || n == 2 || n == 3) i = 0; if(n == 4 || n == 5 || n == 6 || n == 7) i = (PI / 2); if(n == 0 || n == 4 || n == 1 || n == 5) j = -1; else j = 1; x_3[n] = (int)(j * sin(phi - i) * cos(theta + n * PI / 2) * alpha); y_3[n] = (int)(j * sin(phi - i) * sin(theta + n * PI / 2) * alpha); z_3[n] = (int)(j * alpha * cos(phi - i)); } break; } } // *************************************************** // convert 3d points to 2d for display // this could probably be done with the calculations // and eliminate the need for the arrays of 3d // points, but that gets complicated. // this is a simple translation, no arrays here. void flatten(int x_2[], int y_2[], int x_3[], int y_3[], int z_3[]){ double m = 150.0; int n; for(n = 0; n < 8; n++){ x_2[n] = (int)((m * x_3[n])/(z_3[n] + m)); y_2[n] = (int)((m * y_3[n])/(z_3[n] + m)); } // a little testing code to see the figures in 2d. /* for(n = 0; n < 8; n++){ x_2[n] = x_3[n]; y_2[n] = y_3[n]; }*/ } // *************************************************** // draw everything! void draw_cube(int x_2[], int y_2[], int x_3[], double theta, double dtheta, int color, int y_3[], int z_3[]){ // for testing. displays various useful values below the figure on screen. //GP142_printfXY (WHITE, -200, -180, 16, "theta: %.2f theta + PI: %.2f cos1: %.2f cos2: %.2f", theta, theta + PI, cos(theta), cos(theta + PI)); //GP142_printfXY (WHITE, -200, -200, 16, "x_3[0]: %d y_3[0]: %d z_3[0]: %d", x_3[0], y_3[0], z_3[0]); //GP142_printfXY (WHITE, -200, -220, 16, "x_3[4]: %d y_3[4]: %d z_3[4]: %d", x_3[4], y_3[4], z_3[4]); GP142_lineXY(color, x_2[0], y_2[0], x_2[1], y_2[1], 2); GP142_lineXY(color, x_2[0], y_2[0], x_2[3], y_2[3], 2); GP142_lineXY(color, x_2[0], y_2[0], x_2[4], y_2[4], 2); GP142_lineXY(color, x_2[1], y_2[1], x_2[2], y_2[2], 2); GP142_lineXY(color, x_2[1], y_2[1], x_2[5], y_2[5], 2); GP142_lineXY(color, x_2[2], y_2[2], x_2[3], y_2[3], 2); GP142_lineXY(color, x_2[2], y_2[2], x_2[6], y_2[6], 2); GP142_lineXY(color, x_2[3], y_2[3], x_2[7], y_2[7], 2); GP142_lineXY(color, x_2[4], y_2[4], x_2[7], y_2[7], 2); GP142_lineXY(color, x_2[4], y_2[4], x_2[5], y_2[5], 2); GP142_lineXY(color, x_2[5], y_2[5], x_2[6], y_2[6], 2); GP142_lineXY(color, x_2[6], y_2[6], x_2[7], y_2[7], 2); }