Chris Pollett > Old Classes
> |
HW3 Solutions PageBook Problems5.2We want to show R(a)R(b) = R(a+b). The Book doesn't say if 2D or 3D rotation but since no axis given we assume it is 2D. We'll also use 2x2 matrices. (The homogeneous coordinates proof follows trivially from this case.) First, we compute R(a)R(b): == = = == == | cos a sin a | | cos b sin b | | -sin a cos a | | -sin b cos b | = == = = == == == == | (cos a*cos b - sin a*sin b) (cos a*sin b + sin b*cos a) | | -(sin a* cos b + cos a*sin b) (cos a*cos b - sin a*sin b ) | == == Using the identities: cos(a+b) = cos a*cos b - sin a*sin b and sin(a+b) = sin a*cos b + cos a*sin b This shows the diagonala of this product matrix are cos(a+b) and the off diagonals are -sin(a+b) in the first column and sin(a+b) in the second. QED 5.5We are asked to show uniform scalings and rotations commute with each other. == == == == | s 0 | | cos b sin b | | 0 s | | -sin b cos b | = == == == == == == == == == == | s*cos b s*sin b | | cos b sin b | | s 0 | | -s*sin b s*cos b | = | -sin b cos b | | 0 s | == == == == == == So these operations commute with each other. We are then asked to show if the scaling is not uniform the two operations do not commute. To show this it suffices to come up with some non-uniform scaling and some rotation such that the two operations do not commute. Consider: == == == == == == | 1 0 | | cos 90 sin 90 | | 0 1 | | 0 2 | | -sin 90 cos 90 | = | -2 0 | == == == == == == We are using cos 90 = 0 and sin 90 = 1. On the other hand, == == == == == == | cos 90 sin 90 | | 1 0 | | 0 2 | | -sin 90 cos 90 | | 0 2 | = | -1 0 | == == == == == == aliastrans.cpp/****************************************************** * Project: CS116A Homework #3 * File: aliastrans.cpp * Purpose: Code to draw an anti-aliased, translated, rotated, scaled house using OpenGL * Start date: Nov 14, 2004 * Programmer: Chris Pollett * * Remarks: * In real world word have separate .h and .cpp files * *******************************************************/ #ifdef WIN32 #include<windows.h> //for windows #include <GL/glut.h> #include <GL/glu.h> #endif #ifdef __APPLE__ //for MAC /* I created my project under xcode. I chose new C++ tool as the kind of project. Then under External frameworks and libraries I added the two frameworks: OpenGL.framework and GLUT.framework. (Frameworks are in /Library/Frameworks) */ #include <OpenGL/gl.h> #include <OpenGL/glu.h> #include <GLUT/glut.h> #endif #ifdef linux // for linux /*My compile line was: g++ -I /usr/X11R6/include -L /usr/X11R6/lib -lglut -lGL \ -lGLU -lX11 -lXmu -lXi -lm name.cpp -o name */ #include <GL/glut.h> #include <GL/glu.h> #endif #include <cstdlib> #include <cmath> #include <iostream> #include <fstream> using namespace std; /* CONSTANTS */ const GLfloat PI = 3.14159265358979323846; const int PIXELWIDTH = 10; //how many subpixels wide for super-sampling //const GLfloat intensityInc = 1.0/(PIXELWIDTH*PIXELWIDTH); /* This is how much each subpixel will add to the intensity. As the PIXELWIDTH in subpixels gets larger this will result in fainter and fainter lines (as the line is getting thinner and thinner with respect the pixel). One can compensate for this by always drawing line with a certain fraction of a pixel thickness or trying to use pixel masks. */ const GLfloat intensityInc = 1.0/PIXELWIDTH; // this looks better but isn't books algorithm const int ZERO = 0; //Some constants used by out matrix constructor const int IDENTITY = 1; const int TRANSLATION = 2; const int ROTATION = 3; const int SCALE = 4; /* CLASS DEFINITIONS */ /*-----------------------------------------------*/ class Pt /* PURPOSE: Encapsulates the notions of a point (x,y) on the screen Has simple accessors and increment/decrement mutators which are useful for Bresenham-like algorithms */ { private: GLint x, y; public: Pt() { x= y =0; } Pt(GLint x0, GLint y0) { x = x0; y = y0; } void setCoords(GLint x0, GLint y0) { x = x0; y = y0; } GLint getx() const { return x; } GLint gety() const { return y; } void incx() { x++; } void decy() { y--; } }; class Matrix /* PURPOSE: Encapsulates the notions of a 2D homogeneous coordinate transformation matrix*/ { private: GLfloat entry[3][3]; void clear() { for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { entry[i][j] = 0; } } } public: Matrix(int type = ZERO, GLfloat param1 = 0, GLfloat param2 = 0); friend Matrix operator*(Matrix m1, Matrix m2); friend Pt operator*(Matrix m1, Pt p1); }; /* GLOBALS */ GLsizei winWidth = 600, winHeight = 300; // used for size of window Matrix gTransformation; //Transformation matrix to apply to drawing /* FUNCTIONS */ // Auxiliary functions void setPixel(GLint x, GLint y); inline GLfloat toDegrees(GLfloat radians); inline GLint crossProduct(GLint x0, GLint y0, GLint x1, GLint y1); void reflectOctant(Pt pt, Pt& presult, int octant); void lineBres(int x0, int y0, int xEnd, int yEnd); // Polyline functions void polyLineBres(int x[], int y[], int len); // Arc functions void circleArcMidpoint(GLint xc, GLint yc, GLint r, GLfloat startTheta = 0, GLfloat endTheta = 2*PI); //added some default values for angles //Additional graphics functions that use our HW1 functions to draw things void drawPolygon(Pt p[], int start, int end); void drawCircle(Pt leftDiameter, Pt rightDiameter); /* IMPLEMENTATIONS */ //Class Member Functions /*-----------------------------------------------*/ Matrix::Matrix(int type, GLfloat param1, GLfloat param2) /* PURPOSE: Constructs a 2D coordinate transformation matrix (as a 3x3 homogeneous matrix) RECEIVES: type - type of transformation to construct (IDENTITY, TRANSLATION, ROTATION, SCALE) param1, param2 -- parameters which depend on the type of transformation we are doing RETURNS: constructed matrix REMARKS: */ { int i; switch(type) { case TRANSLATION: clear(); for(i = 0; i < 3 ; i++) { entry[i][i] = 1; } entry[0][2] = param1; entry[1][2] = param2; break; case ROTATION: //counter-clockwise clear(); entry[0][0] = cos(param1); entry[0][1] = -sin(param1); entry[1][0] = -entry[0][1]; entry[1][1] = entry[0][0]; entry[2][2] = 1; break; case SCALE: //only do uniform scaling clear(); entry[0][0] = param1; entry[1][1] = param1; entry[2][2] =1; break; case IDENTITY: clear(); for(i = 0; i < 3 ; i++) { entry[i][i] = 1; } break; case ZERO: default: clear(); } } Matrix operator*(Matrix m1, Matrix m2) { Matrix result(ZERO); for(int i = 0 ; i < 3; i++) { for(int j = 0; j < 3; j++) { for(int k = 0; k < 3; k++) { result.entry[i][j] += m1.entry[i][k]*m2.entry[k][j]; } } } return result; } Pt operator*(Matrix m, Pt p) { GLfloat homoPt[3] = {p.getx(), p.gety(), 1.0}; GLfloat resultPt[3] = {0.0, 0.0, 0.0}; Pt result; for(int i = 0 ; i < 2; i++) { for(int k = 0; k < 3; k++) { resultPt[i] += m.entry[i][k]*homoPt[k]; } } result.setCoords((GLint)resultPt[0], (GLint)resultPt[1]); return result; } //Usual Functions /*-----------------------------------------------*/ inline GLfloat toDegrees(GLfloat radians) /* PURPOSE: converts an angle in radians to degrees RECEIVES: radians -- the angle to convert RETURNS: the equivalent value in degrees REMARKS: This is only used in the test angle drawing function since this is based on a GLU call in degrees Not used in the official angle drawing function inlined so don't have stack overhead in doing this small call */ { return 180*radians/PI; } /*-----------------------------------------------*/ void setPixel(GLint x, GLint y) /* PURPOSE: Draw a pixel at desired location on screen RECEIVES: x,y - coordinates of pixel to draw RETURNS: nothing REMARKS: side-effect is pixel drawn */ { glBegin(GL_POINTS); glVertex2i(x, y); glEnd(); } /*-----------------------------------------------*/ inline GLint crossProduct(Pt p0, Pt p1) /* PURPOSE: Takes the 2D cross-product of supplied points. RECEIVES: p0, p1 - points to take cross product of RETURNS: p0 X p1 REMARKS: inlined for speed */ { return p0.getx()*p1.gety() - p1.getx()*p0.gety(); } /*-----------------------------------------------*/ void reflectOctant(Pt pt, Pt& presult, int octant) /* PURPOSE: By using reflections to convert a point in the first octant to a point in the desired octant RECEIVES: pt - a point assummed to be in the first quadrant presult - a reference to a point to store the result octant - desired octant to convert point to RETURNS: nothing REMARKS: */ { switch(octant%8) { case 0: presult.setCoords(pt.getx(), pt.gety()); break; case 1: presult.setCoords(pt.gety(), pt.getx()); break; case 2: presult.setCoords(pt.gety(), - pt.getx()); break; case 3: presult.setCoords(pt.getx(), - pt.gety()); break; case 4: presult.setCoords( - pt.getx(), - pt.gety()); break; case 5: presult.setCoords( - pt.gety(), - pt.getx()); break; case 6: presult.setCoords( - pt.gety(), pt.getx()); break; case 7: presult.setCoords( - pt.getx(), pt.gety()); break; } } /*-----------------------------------------------*/ void lineBres(int x0, int y0, int xEnd, int yEnd) /* PURPOSE: Draws a line segment from (x0, y0) to (xEnd, yEnd) using my implementation Bresenham's algorithm and anti-aliasing RECEIVES: x0, y0 - start point of line segment xEnd, yENd - end point of line segment RETURNS: nothing REMARKS: Side-effect is the line segment drawn Note basically hacked the books code to handle negative slopes and slopes with large magnitude. */ { int dx = PIXELWIDTH*abs(xEnd - x0), dy = PIXELWIDTH*abs(yEnd - y0); //PIXELWIDTH factors are for anti-aliasing int p; int twoD, twoDMinusD; int x,y, *a, *b, *a0, *aEnd; int slopeSign = ((xEnd - x0)*(yEnd - y0) > 0) ? 1 : -1; if(dx > dy)//added by me to books algorithm { a = &x; b = &y; a0 = &x0; aEnd = &xEnd; p = 2*dy -dx; twoD = 2*dy; twoDMinusD = 2*(dy-dx); } else { a = &y; b = &x; a0 = &y0; aEnd = &yEnd; p = 2*dx - dy; twoD = 2*dx; twoDMinusD = 2*(dx-dy); } if(*a0 > *aEnd) { x = xEnd; y = yEnd; *aEnd = *a0; } else { x = x0; y = y0; } GLfloat intensity = intensityInc; int subpixela = 0; int subpixelb = 0; int nextA = *a; int nextB = *b; bool plot = false; glEnable(GL_BLEND); //blending is being used for anti-aliasing glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); while(*a < *aEnd) { subpixela++; intensity += intensityInc; if(subpixela == PIXELWIDTH) // only draw when cross a pixel boundary { subpixela = 0; plot = true; nextA++; } if(p < 0) { p += twoD; } else { subpixelb++; p += twoDMinusD; if(subpixelb == PIXELWIDTH)// only draw when cross a pixel boundary { subpixelb = 0; plot = true; nextB +=slopeSign; } } if(plot) { plot = false; glColor4f(0.0, 0.0, 0.0, intensity); setPixel(x, y); intensity = 0; *a = nextA; *b = nextB; } } glDisable(GL_BLEND); } /*-----------------------------------------------*/ void polyLineBres(int x[], int y[], int len) /* PURPOSE: Draws a polyline using my implmentation of Bresenham's algorithm RECEIVES: x[], y[] - arrays of x and y values for points in the polyline len - number of points in the polyline RETURNS: nothing REMARKS: Has side-effect of drawing the poly-line to the screen */ { for(int i = 0; i < len - 1; i++) { lineBres(x[i], y[i], x[i+1], y[i+1]); } } /*-----------------------------------------------*/ void circleArcMidpoint(GLint xc, GLint yc, GLint r, GLfloat startTheta, GLfloat endTheta) /* PURPOSE: My implementation of a function to draw an arc using circle midpoint algorithm centered at xc,yc of radius r from startTheta in radians to endTheta in radians. Arc drawing is down using anti-aliasing RECEIVES: xc, yc - center of arc r - radius of arc startTheta - starting angle in radians endTheta - ending angle in radians RETURNS: nothing REMARKS: Side-effect is that arc drawn. Note I was a little sloppy about boundaries of octants O radians on our unit circle corresponds to the point (0, 1) and we take our angles clockwise around the circle */ { /* Variable initializations. Want to recalculate startTheta and endTheta so that startTheta + 2PI > endTheta >+ startTheta and 2PI > startTheta >= 0 Also want to calculate which octants our angle is in and what are the endpoints of the arc. Although we do use sin and cos, we do this a total of 4 times, two of which can be eliminated, so incur at most a constant overhead to the midpoint algorithm. */ Pt pt, oldPt, drawPt, nextDrawPt; Pt startPt, endPt; //start and end points of arc Pt endStartOctantPt, startEndOctantPt; /* corresponding end and start points of these points octants */ Pt tmpPt; GLint rsTheta = (int)(startTheta/(2*PI)); int startOctant; int endOctant; startTheta -= rsTheta*(2*PI); endTheta -= rsTheta*(2*PI); startTheta = (startTheta > 0) ? startTheta : 2*PI - startTheta; endTheta = (endTheta > 0) ? endTheta : 2*PI - endTheta; endTheta = (endTheta > startTheta) ? endTheta : endTheta + (2*PI); startOctant = (int)(4*startTheta/PI); endOctant = (int)(4*endTheta/PI); startPt.setCoords((long)(r*sin(startTheta)), (long)(r*cos(startTheta))); endPt.setCoords((long)(r*sin(endTheta)), (long)(r*cos(endTheta))); if(startOctant == endOctant) { endStartOctantPt = endPt; startEndOctantPt = startPt; } else { /* the use of sin and cosine here could be replaced by a lookup table as everything is a multiple of 4 */ endStartOctantPt.setCoords((int)(r*sin(PI*(startOctant+1)/4)), (int)(r*cos(PI*(startOctant+1)/4))); startEndOctantPt.setCoords((int)(r*sin(PI*(endOctant)/4)), (int)(r*cos(PI*(endOctant)/4))); } // initialize decision parameters and first octant point pt.setCoords(0, r*PIXELWIDTH); // PIXELWIDTH comes from doing anti-aliasing oldPt.setCoords(0, r*PIXELWIDTH); drawPt.setCoords(0, r); nextDrawPt.setCoords(0, r); GLint p = 1 - r*PIXELWIDTH; GLfloat intensity = intensityInc; bool plot = false; glEnable(GL_BLEND); //blending is being used for anti-aliasing glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //Here is where the drawing is actually done while(drawPt.getx() < drawPt.gety()) { pt.incx(); intensity += intensityInc; if(pt.getx() == oldPt.getx() + PIXELWIDTH) { oldPt.setCoords(pt.getx(), oldPt.gety()); nextDrawPt.incx(); plot = true; } if(p < 0) { p += 2 * pt.getx() + 1; } else { pt.decy(); p += 2 * (pt.getx() - pt.gety ()) + 1; if(pt.gety() == oldPt.gety() - PIXELWIDTH) { oldPt.setCoords(oldPt.getx(), pt.gety()); nextDrawPt.decy(); plot = true; } } if(plot) { glColor4f(0.0, 0.0, 0.0, intensity);//set draw color based on how filled pixel was plot = false; // reset plot mode and intensity intensity = 0; //draw points, if applicable, in starting octant reflectOctant(drawPt, tmpPt, startOctant); if(crossProduct(startPt, tmpPt)* crossProduct(tmpPt, endStartOctantPt) > 0) { setPixel(xc + tmpPt.getx(), yc + tmpPt.gety()); } //draw points, if applicable, in ending octant reflectOctant(drawPt, tmpPt, endOctant); if(crossProduct(startEndOctantPt, tmpPt)* crossProduct(tmpPt, endPt) > 0) setPixel(xc + tmpPt.getx(), yc + tmpPt.gety()); /* ASIDE: notice if starting and ending octant were the same, then by how we initialized things we would have plotted the same points twice above. */ //draw points in all the octants we know have to be completely drawn for(int i = startOctant+1; i < endOctant; i++) { reflectOctant(drawPt, tmpPt,i); setPixel(xc + tmpPt.getx(), yc + tmpPt.gety()); } drawPt = nextDrawPt; } } glDisable(GL_BLEND); } /*-----------------------------------------------*/ void drawPolygon(Pt p[], int start, int end) /* PURPOSE: Draws a polygon using polyLineBres RECEIVES: p - array of points in polygon start -starting index in p of polygon end -ending index in p of polygon RETURNS: nothing REMARKS: Has side-effect of drawing a polygon to the screen */ { int len = end - start; int x[len + 2], y[len + 2]; for(int i = 0; i<=len; i++) { x[i] = p[start+i].getx(); y[i] = p[start+i].gety(); } x[len + 1] = x[0]; //connect back to start y[len + 1] = y[0]; polyLineBres(x, y, len + 2); } /*-----------------------------------------------*/ void drawCircle(Pt leftDiameter, Pt rightDiameter) /* PURPOSE: Draws a circle using circleArcMidpoint such that the two points supplies are on a diameter of this circle RECEIVES: leftDiameter- left coordinates of the diameter of circle rightDiameter- right coordinates of the diameter of circle RETURNS: nothing REMARKS: Has side-effect of drawing a circle to the screen */ { int x = (leftDiameter.getx() + rightDiameter.getx())/2; int y = (leftDiameter.gety() + rightDiameter.gety())/2; int diffX = rightDiameter.getx() - x; int diffY = rightDiameter.gety() - y; int radius = (int)sqrt((float)(diffX*diffX + diffY*diffY)); circleArcMidpoint(x, y, radius); // using default values for angles; } /* GLUT AND DRIVER CODE */ /*-----------------------------------------------*/ void readParameters() /* PURPOSE: Reads Parameter file with initial window size and two points position RECEIVES: nothing RETURNS: nothing REMARKS: */ { ifstream fin; GLfloat transX, transY, angle, scale; try { fin.open("parameters.txt"); if(fin.fail()) throw string("Failed to open parameters.txt"); if(!(fin >> winWidth >> winHeight >> transX >> transY >> angle >> scale)) { fin.close(); throw string("parameters.txt format incorrect"); } gTransformation = Matrix(SCALE, scale)* Matrix(ROTATION, 2*PI*angle/360)*Matrix(TRANSLATION, transX, transY); fin.close(); } catch(string error) { cerr << error << endl; cerr << "Using default values" << endl; gTransformation = Matrix(IDENTITY); } } /*-----------------------------------------------*/ void init(void) /* PURPOSE: Used to initializes our OpenGL window RECEIVES: Nothing RETURNS: Nothing REMARKS: Nothing */ { glClearColor(1.0, 1.0, 1.0, 0.0); glMatrixMode(GL_PROJECTION); gluOrtho2D(0.0, winWidth, 0.0, winHeight); } /*-----------------------------------------------*/ void drawHouse(void) /* PURPOSE: GLUT display callback function for this program. Serves as the main driver for the program. Draws transformed house RECEIVES: nothing, although uses global variables that have been set up with data from the parameters.txt file RETURNS: nothing REMARKS: Side-effect is to draw a house with anti-aliased line according to the transformations from the paremeter.txt file */ { glClear(GL_COLOR_BUFFER_BIT); glColor4f(0.0, 0.0, 0.0, 0.0); // //here's where we draw the strings to the screen Pt myHouse[] = { Pt(10, 10), Pt(10, 100), Pt(100, 100), Pt(100, 10), //square Pt(10, 100), Pt(55, 150), Pt(100, 100), //triangle Pt(200, 200), Pt(250, 200)}; //circle int myHouseLen = 9; for(int i = 0; i< myHouseLen; i++) { myHouse[i] = gTransformation*myHouse[i]; } drawPolygon(myHouse, 0, 3); drawPolygon(myHouse, 4, 6); drawCircle(myHouse[7], myHouse[8]); glFlush(); } /*-----------------------------------------------*/ void winReshapeFn(int newWidth, int newHeight) /* PURPOSE: Resizes/redisplays the contents of the current window RECEIVES: newWidth, newHeight -- the new dimensions RETURNS: nothing REMARKS: */ { //glViewport(0, 0, newWidth, newHeight); glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluOrtho2D(0.0, winWidth, 0.0, winHeight); drawHouse(); } int main(int argc, char** argv) { readParameters(); glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowPosition(50, 100); glutInitWindowSize(winWidth, winHeight); glutCreateWindow("Anti-alias and Transformation Tester"); init(); glutDisplayFunc(drawHouse); glutReshapeFunc(winReshapeFn); glutMainLoop(); return 0; } |