/* CS 365 Triangle Lab Skeleton * */ #include #include #include #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y)) #define MAX(X,Y) (((X) > (Y)) ? (X) : (Y)) #define TWOPI (2*3.1415926535) /*------------------------------------------ * Standard functions and callbacks *------------------------------------------ */ /* Initialize GL */ void initGLUT(); /* Called while program is idle */ void idle(); /* Redraws the whole image */ void display(); /* Called when a regular key is pressed */ void keyEvent(unsigned char key, int x,int y); /* Called when a special key is pressed (e.g. arrow key) */ void specialKeyEvent(int key, int x, int y); /* Called when mouse button pressed */ void mouseButton(int button, int state, int x, int y); /* Called when the mouse moves */ /* void mouseMotion(int x, int y); */ /* Called out of idle loop */ /* void idle(void); */ /*---------------------------------------------------------- * USER DATA TYPES *---------------------------------------------------------- */ /* The location of a single point */ typedef struct { int x, y; } point; /* Struct to hold a single pixel (as three color bytes) */ typedef struct { unsigned char r, g, b; /* use 256 colors */ } rgb_pixel; /* 3 x 3 matrix */ typedef double mat[3][3]; /* A triangle has 3 points, 3 colors */ typedef struct { point A, B, C; /* Original coordinates of the three corners */ rgb_pixel Ca, Cb, Cc; /* Colors of same */ point A1, B1, C1; /* Transformed addresses */ int xmove; /* X movement from original spot (pixels) */ int ymove; /* Y movement from original spot (pixels) */ int rot; /* Rotation angle (degrees) */ mat rotMat; /* Rotation matrix */ mat moveMat; /* Translation matrix */ mat xformMat; /* rot x xlate */ double falpha; /* Implicit lines for barycentric computation*/ double fbeta; double fgamma; } triangle; /*-------------------------------------- * USER FUNCTION FORWARD DECLARATIONS *-------------------------------------- */ void colorit(int x, int y, rgb_pixel c); void draw_line(point p0, point p1, rgb_pixel c); void draw_triangle_interior(int t); void fill_triangle(point a, point b, point c, rgb_pixel ca, rgb_pixel cb, rgb_pixel cc); triangle * new_triangle(point a, point b, point c, rgb_pixel ca, rgb_pixel cb, rgb_pixel cc); rgb_pixel rcolor(point p); void draw_triangle(int t); void draw_all_triangles(void); int in_triangle(point a, point b, point c, point p); void free_triangle(triangle *T); void make_identity(mat * m); void draw_plus(void); void mat_mult(mat * A, mat * B, mat * rslt); void print_matrix(int howmuch); void print_mat(mat M); point point_scale(mat A, point p); void adjust_rotation(int degree); void draw_partial_triangle(void); void reset_xform(void); void adjust_xlate(int xmove, int ymove); /*----------------------------------------------------------- * GLOBAL VARIABLES *----------------------------------------------------------- */ /* Array of triangle structs. */ #define TRIMAX 10 triangle * (triangles[TRIMAX]); int ntriangles = 0; /* Used for accumulating three points to make one triangle */ point point_a, point_b, point_c; int points_accumulated = 0; /* Dimensions of the screen */ #define SCREENWIDTH 601 #define SCREENHEIGHT 501 const int screenWidth = SCREENWIDTH; const int screenHeight = SCREENHEIGHT; /* Background color */ rgb_pixel bgcolor = {210, 210, 255}; /* Light blue */ /* Line color */ rgb_pixel edge_color = {0, 0, 0}; /* Black */ /* Pixel buffer representing the whole picture */ rgb_pixel canvas[SCREENWIDTH * SCREENHEIGHT]; /* The LOC macro converts (x,y) to an index into this buffer */ #define LOC(X,Y) ((Y)*screenWidth + (X)) /* Macro to check for x,y out of canvas bounds */ #define OBOUND(X,Y) (((X) < 0) | ((X) >= SCREENWIDTH) | ((Y) < 0) | ((Y) >= SCREENHEIGHT)) /*--------------------------------------------------- * MAIN PROGRAM *---------------------------------------------------*/ int main(int argc, char **argv) { int i; /* Initialize glut */ glutInit(&argc,argv); /* Initialize GLUT state, window, callbacks */ initGLUT(); /* Makes each pixel the background color. */ for(i = 0; i < screenWidth*screenHeight; i++) { canvas[i] = bgcolor; } /* Put plus in middle */ draw_plus(); /* event loop starts here */ glutMainLoop(); return 0; } /*---------------------------------------------------------------------- * initGLUT *---------------------------------------------------------------------*/ void initGLUT() { /* use red/green/blue, double buffered */ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB ); /* Create Main Graphics Window */ glutInitWindowSize(screenWidth, screenHeight); glutInitWindowPosition(200,100); glutCreateWindow("CS 365 Triangle Lab"); /* initializes callbacks for glut */ glutDisplayFunc(display); glutMouseFunc(mouseButton); glutKeyboardFunc(keyEvent); glutSpecialFunc(specialKeyEvent); /* Make sure first display is called */ glutPostRedisplay(); } /*-------------------------------------------------------------------- * Idle *--------------------------------------------------------------------*/ /* Redraw the display window in the idle loop */ /* This lab does not need continuous redraw, so it is never called */ void idle() { /* Redraw the contents of the display window */ glutPostRedisplay(); } /*---------------------------------------------------------------------- * display * Re-create the image according to the current conditions and cause * it to be displayed again. *----------------------------------------------------------------------*/ void display() { /* Debugging: print a counter every time we redraw the scene */ static int entry=0; printf("%d\n", entry++); /* Render the image as pixels */ draw_all_triangles(); draw_partial_triangle(); draw_plus(); /* Give the pixel buffer we have created to OpenGL */ /* Rows of pixel data are all packed together (byte aligned) */ /* glPixelStorei(...) ??? */ /* glDrawPixels(...) ??? */ /* Swap the double buffers */ glutSwapBuffers(); /* Flush all drawing to the screen */ glFlush(); } /*---------------------*/ /* Normal key handler */ /*---------------------*/ void keyEvent(unsigned char key,int x,int y) { switch (key) { /* Quit */ case 'Q': case 'q': exit(0); break; /* Erase top triangle */ case 'E': case 'e': if (ntriangles > 0) { triangle * T = triangles[--ntriangles]; free_triangle(T); }; break; /* Rotate top triangle by a small fixed angle */ case 'r': adjust_rotation(-5); break; case 'R': adjust_rotation(5); break; /* Print top triangle current transformation matrix */ case 'm': print_matrix(0); break; case 'M': print_matrix(1); break; /* Do nothing on the default other keys */ default: break; } glutPostRedisplay(); } /*---------------------------------------------------------------------*/ /* specialKeyEvent * Processes special keys (controls, arrows, etc.) *---------------------------------------------------------------------*/ #define KEY_DELTA 0.5 void specialKeyEvent(int key, int x, int y) { int ctrlon = glutGetModifiers() & GLUT_ACTIVE_CTRL; switch(key) { /* 2D motion using arrow keys */ case GLUT_KEY_LEFT: /* ??? */ break; case GLUT_KEY_RIGHT: /* ??? */ break; case GLUT_KEY_UP: /* ??? */ break; case GLUT_KEY_DOWN: /* ??? */ break; /* Page up/down unused */ case GLUT_KEY_PAGE_UP: break; case GLUT_KEY_PAGE_DOWN: break; /* Home key resets the top triangle to original position */ case GLUT_KEY_HOME: reset_xform(); break; } glutPostRedisplay(); } /*-------------------------------------------------------------------- * Move the selected triangle on the top of the stack *--------------------------------------------------------------------*/ void shift_stack(int t) { int i; triangle * T; T = triangles[t]; for (i=t+1; i<=(ntriangles-1); i++) { triangles[i-1] = triangles[i]; } triangles[ntriangles-1] = T; return; } /*--------------------------------------------------------- * Mouse Button Handler *---------------------------------------------------------*/ void mouseButton(int button,int state,int xm,int ym) { int x, y; glutPostRedisplay(); /* Adjust from screen coordinates to the displayed coordinates */ /* (y increases going up, (0,0) is in the center */ /* x, y = function of xm, ym ??? */ /* The "left" (major) button: * Three successive clicks draws the next triangle. */ if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { if (ntriangles >= TRIMAX) return; switch (points_accumulated) { case 0: point_a.x = x; point_a.y = y; break; case 1: point_b.x = x; point_b.y = y; break; case 2: { triangle * T; point_c.x = x; point_c.y = y; T = new_triangle(point_a, point_b, point_c, rcolor(point_a), rcolor(point_b), rcolor(point_c)); triangles[ntriangles++] = T; reset_xform(); break; } } points_accumulated = (points_accumulated+1) % 3; } /* The "right" (minor) button: * If the click was inside a triangle, bring it to the * top of the stack. */ if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) { int t; point p; p.x = x; p.y = y; for (t = 0; t <= ntriangles-1; t++) { if (in_triangle(triangles[t]->A1, triangles[t]->B1, triangles[t]->C1, p)) { shift_stack(t); return; } } } } /*----------------------------------------------------- * Display the partial triangle under construction * (if there is any), depends on the number of clicks * accumulated so far: * * Zero clicks: do nothing * One click: display a small x a the one point * Two clicks: display a line between the two points */ void draw_partial_triangle(void) { switch (points_accumulated) { case 1: { /* Draw a small 'x' as two lines, be sure not to exceed the bounds of the picture. */ break; } case 2: { draw_line(point_a, point_b, edge_color); break; } } /* switch */ return; } /*----------------------------------------------------- * mouseMotion *-----------------------------------------------------*/ /* Called when the mouse moves */ /* No mouse movement in this lab */ void mouseMotion(int x,int y) { y = screenHeight - y; if ((x < 0) | (x >= screenWidth) | (y < 0) | (y >= screenHeight)) return; /* Mouse motion goes here */ return; } /*-----------------------------------------------------*/ /* Creates pseudo-random color based on (x,y) position */ /*-----------------------------------------------------*/ rgb_pixel rcolor(point p) { rgb_pixel c = {0, 0, 0}; /* ??? */ return c; } /*---------------------------------------------------------*/ /* Resets the transformations in the top triangle to unity */ /*---------------------------------------------------------*/ void reset_xform(void) { triangle * T; if (ntriangles <= 0) return; T = triangles[ntriangles-1]; T->xmove = 0; T->ymove = 0; T->rot = 0; make_identity(&T->rotMat); make_identity(&T->moveMat); make_identity(&T->xformMat); return; } /*-------------------------------------------*/ /* Clears the screen and draws all triangles */ /*-------------------------------------------*/ void draw_all_triangles(void) { int t; /* Clear screen to background color ??? */ /* Draw all triangles */ for (t=0; trotMat, &T->moveMat, &T->xformMat); /* Scale the three coordinates of the triangle's vertices */ T->A1 = point_scale(T->xformMat, T->A); T->B1 = point_scale(T->xformMat, T->B); T->C1 = point_scale(T->xformMat, T->C); /* Draw interior first */ draw_triangle_interior(t); /* Draw three edges second */ draw_line(triangles[t]->A1, triangles[t]->B1, edge_color); draw_line(triangles[t]->A1, triangles[t]->C1, edge_color); draw_line(triangles[t]->B1, triangles[t]->C1, edge_color); return; } /*-----------------------------------------------------*/ /* Plots a single point into the canvas (pixel buffer) /*-----------------------------------------------------*/ /* Notes: must tranform from model coordinates to pixel buffer coordinates */ /* must not exceed the row or column maximum or minimum */ void colorit(int x, int y, rgb_pixel c) { /* Your code here ??? */ canvas[LOC(x+(SCREENWIDTH/2), y+(SCREENHEIGHT/2))] = c; return; } /*------------------------------------------------------------*/ /* Draw a line between two points using Bresenham's algorithm */ /*------------------------------------------------------------*/ void draw_line(point p0, point p1, rgb_pixel c) { /* Have fun! ??? */ return; } /* Implict line equation: * * double fline(int x, int y, point P, point Q) * * Plugs the coordinates (X,Y) into the implicit line function * defined by points P and Q. * * Result indicates how far (X,Y) is from the line. * * Thus if your triangle points are A1, B1, C1, you can compute: * alpha(x,y) = fline(x, y, B1, C1) / fline(A1.x, A1.y, B1, C1) */ /* ??? More Fun! */ /*----------------------------------------------------*/ /* Allocate a new triangle structure and fill it in. */ /*----------------------------------------------------*/ triangle * new_triangle(point a, point b, point c, rgb_pixel ca, rgb_pixel cb, rgb_pixel cc) { triangle * T = (triangle *)malloc(sizeof(triangle)); if (T == NULL) {perror(NULL); exit(0);} T->A = a; T->B = b; T->C = c; T->Ca=ca; T->Cb=cb; T->Cc=cc; T->falpha = /* ??? */ T->fbeta = /* ??? */ T->fgamma = /* ??? */ return T; } /*-------------------------------------------------*/ /* Draw the interior of a triangle on the screen. */ /*-------------------------------------------------*/ void draw_triangle_interior(int t) { triangle *T = triangles[t]; fill_triangle(T->A1, T->B1, T->C1, T->Ca, T->Cb, T->Cc); return; } /* Fills a triangle, interpolating the colors */ void fill_triangle(point a, point b, point c, rgb_pixel ca, rgb_pixel cb, rgb_pixel cc) { /* ??? JOY! */ return; } /*------------------------------------------------------------*/ /* Determine whether point p is within a particular triangle */ /*------------------------------------------------------------*/ int in_triangle(point a, point b, point c, point p) { /* ??? HAPPINESS! */ return true; /* Dummy return */ } /*----------------------------*/ /* Destroy an unused triangle */ /*----------------------------*/ void free_triangle(triangle * T) { free(T); return; } /*-------------------------*/ /* Make an identity matrix */ /*-------------------------*/ void make_identity(mat * m) { int i, j; for(i=0; i<3; i++){ for (j=0; j<3; j++) { (*m)[i][j] = 0.0; } (*m)[i][i] = 1.0; } return; } /* Draw a small plus at origin. * * This routine is written to access the canvas directly, * not calling any subroutines for scaling coordinates * drawing points or lines, so it can be included with the * skeleton. * * The (0,0) point of the model-coordinate space is at * (width/2, height/2) in canvas coordinates. */ void draw_plus(void) { int i; for (i=-5; i<=5; i++) { canvas[LOC((SCREENWIDTH/2), i+(SCREENHEIGHT/2))] = edge_color; canvas[i+LOC((SCREENWIDTH/2), (SCREENHEIGHT/2))] = edge_color; } return; } /*-------------------------------------------------------*/ /* Change the rotation matrix in response to a key press */ /*-------------------------------------------------------*/ void adjust_rotation(int degree) { double theta; triangle * T; if (ntriangles <= 0) return; T = triangles[ntriangles-1]; T->rot += degree; theta = ((double)T->rot / 360.0) * TWOPI; printf("Rot=%d, theta=%f10.2\n", T->rot, theta); /* Update T->rotMat ??? */ } /*------------------------------------------------------*/ /* Adjust translation matrix in response to a key press */ /*------------------------------------------------------*/ void adjust_xlate(int xmove, int ymove) { triangle * T; if (ntriangles <= 0) return; T = triangles[ntriangles-1]; /* Update T->moveMat ??? */ return; } /*---------------------------------------*/ /* Matrix Multiplication of 3x3 matrices */ /*---------------------------------------*/ void mat_mult(mat * A, mat * B, mat * rslt) { mat C; int i, j, k; for (i=0; i<3; i++) { for (j=0; j<3; j++) { C[i][j] = 0; for (k=0; k<3; k++) { C[i][j] += (*A)[i][k] * (*B)[k][j]; } } } for (i=0; i<3; i++) { for (j=0; j<3; j++) { (*rslt)[i][j] = C[i][j]; } } return; } /* Round a double to the nearest integer */ /* Understand that by default negative numbers are *truncated* when * converted to integer: (int) -1.75 == -1 */ int round(double z) { int i; /* ??? Your code here */ return (int) z; /* THIS IS WRONG */ } /*---------------------------------------------------------------*/ /* Scale the location of a point according to the current matrix */ /*---------------------------------------------------------------*/ point point_scale(mat A, point p) { double xf, yf; int x, y; point rslt; x = p.x; y = p.y; xf = A[0][0]*x + A[0][1]*y + A[0][2]*1; yf = A[1][0]*x + A[1][1]*y + A[1][2]*1; rslt.x = round(xf); rslt.y = round(yf); return rslt; } /*--------------------------------------------------------- * Print the matrix of the top triangle. * The 'howmuch' parameter dictates whether to print * only the total matrix or also the component parts *--------------------------------------------------------*/ void print_matrix(int howmuch) { triangle * T; if (ntriangles <= 0) return; T = triangles[ntriangles-1]; switch (howmuch) { case 0 : printf("X-move=%d, Y-move=%d, Rot=%d\n", T->xmove, T->ymove, T->rot); printf("Total Matrix:\n"); print_mat(T->xformMat); break; case 1 : printf("X-move=%d, Y-move=%d, Rot=%d\n", T->xmove, T->ymove, T->rot); printf("Total Matrix:\n"); print_mat(T->xformMat); printf("\nRotation:\n"); print_mat(T->rotMat); printf("\nTranslation\n"); print_mat(T->moveMat); break; } } void print_mat(mat M){ int i; for (i=0; i<3; i++) { printf("%7.2f %7.2f %7.2f\n", M[i][0], M[i][1], M[i][2]); } }