// your #includes here, including canvas.h #define maxVertices 1000 const int screenWidth = 640; const int screenHeight = 480; Canvas cvs(640, 480, "Transforming polygons (the hard way)"); /* VECTOR CLASSES Note: all of this uses homogeneous coordinates, so TwoD points and vectors actually have three coordinates (a 1 in the former case and a 0 in the latter). This meant I had to modify the Point2 class to include an origin: class Point2 { public: Point2() { x = y = 0.0f; origin = 1} // modification Point2(float xx, float yy) { x = xx; y = yy; origin = 1; } void set(float xx, float yy) { x = xx; y = yy; } float getX() { return x; } float getY() { return y; } void draw(void) { glBegin(GL_POINTS); glVertex2f((GLfloat)x, (GLfloat)y); glEnd(); glFlush(); } private: float x, y; int origin; // modification }; */ ///////////// // Vector2 ///////////// class Vector2 { public: Vector2() { x = y = 0.0f; origin = 0; } Vector2(float xx, float yy) { x = xx; y = yy; } void set(float xx, float yy) { x = xx; y = yy; } void setX(float xx) { x = xx; } void setY(float yy) { y = yy; } float getX() { return x; } float getY() { return y; } Vector2 scale(float k) { Vector2 result; result.set(k * x, k * y); return result; } // NB: text does perp & norm differently -- it uses side effects // Also, it has a math error in perpdot on p 835 Vector2 perp() { Vector2 result; result.set(- y, x); return result; } Vector2 normalize() { Vector2 result; double vectorLength = x*x + y*y; if (vectorLength < 0.000000001) { cerr << "\nnormalize() in vector.cxx is about to divide by zero\n"; } float scaleFactor = 1.0/(float)sqrt(vectorLength); result.set(x * scaleFactor, y * scaleFactor); return result; } private: float x, y, origin; }; ///////////// // Vector3 ///////////// class Vector3 { public: Vector3() { x = y = z = 0.0f; origin = 0; } Vector3(float xx, float yy, float zz) { x = xx; y = yy; z = zz; } void set(float xx, float yy, float zz) { x = xx; y = yy; z = zz; } float getX() { return x; } float getY() { return y; } float getZ() { return z; } Vector3 scale(float k) { Vector3 result; result.set(k * x, k * y, k * z); return result; } Vector3 perp() { Vector3 result; result.set(- y, x, z); return result; } Vector3 normalize() { Vector3 result; double vectorLength = x*x + y*y + z*z; if (vectorLength < 0.000000001) { cerr << "\nnormalize() in vector.cxx is about to divide by zero\n"; } float scaleFactor = 1.0/(float)sqrt(vectorLength); result.set(x * scaleFactor, y * scaleFactor, z * scaleFactor); return result; } private: float x, y, z; int origin; }; //-------------------------------------------- // functions that do useful stuff with vectors //-------------------------------------------- Vector2 addVector2s (Vector2 a, Vector2 b) { Vector2 result; result.set(a.getX() + b. getX(), a.getY() + b. getY()); return result; } Vector3 addVector3s (Vector3 a, Vector3 b) { Vector3 result; result.set(a.getX() + b. getX(), a.getY() + b. getY(), a.getZ() + b. getZ()); return result; } float dotVector3s (Vector3 a, Vector3 b) { return ( (a.getX() * b.getX()) + (a.getY() * b.getY()) + (a.getZ() * b.getZ()) ); } float dotVector2s (Vector2 a, Vector2 b) { return ( (a.getX() * b.getX()) + (a.getY() * b.getY())); } Vector3 crossVector3s (Vector3 a, Vector3 b) { Vector3 result; result.set(a.getY()*b.getZ() - b.getY()*a.getZ(), b.getX()*a.getZ() - a.getX()*b.getZ(), a.getX()*b.getY() - b.getX()*a.getY()); return result; } /* 2D TRANSFORMATION MATRIX CLASS didn't work hard on this because OpenGL has built-in stuff to handle this kind of thing automagically */ class TwoDXformMatrix { public: TwoDXformMatrix() { for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { XformArray[i][j]=0.0; } } } TwoDXformMatrix(float m11, float m12, float m13, float m21, float m22, float m23) { XformArray[0][0] = m11; XformArray[0][1] = m12; XformArray[0][2] = m13; XformArray[1][0] = m21; XformArray[1][1] = m22; XformArray[1][2] = m23; XformArray[2][0] = XformArray[2][1] = 0; XformArray[2][2] = 1; } void setElement(int row, int col, float val) { XformArray[row][col] = val; } Vector3 getRow(int row) { Vector3 result; result.set ( XformArray[row][0], XformArray[row][1], XformArray[row][2] ); return result; } Vector3 getCol(int col) { Vector3 result; result.set ( XformArray[0][col], XformArray[1][col], XformArray[2][col] ); return result; } float getElement(int i, int j) { return XformArray[i][j]; } private: float XformArray[3][3]; }; // // drawBox() // // Draw a small box around a point // void drawBox(int x, int y) { cvs.setColor(1.0, 0.0, 0.0); glBegin(GL_LINE_LOOP); glVertex2i(x-1, screenHeight - (y+1)); glVertex2i(x-1, screenHeight - (y-1)); glVertex2i(x+1, screenHeight - (y-1)); glVertex2i(x+1, screenHeight - (y+1)); glVertex2i(x-1, screenHeight - (y+1)); glEnd(); glFlush(); } // // drawPolygon() // // Given a set of vertices, draw lines between them. // void drawPolygon(int numVertices, GLintPoint vertices[]) { cvs.setColor(0.0, 0.0, 1.0); glBegin(GL_LINE_LOOP); for(int i = 0; i < numVertices; i++) glVertex2i(vertices[i].x, vertices[i].y); glEnd(); glFlush(); } // // transform2D() // // Take a vertex, apply a transform to it, and return it. // GLintPoint transform2D(GLintPoint vertex, TwoDXformMatrix Frob) { GLintPoint frobbedVertex; // // Hardcoded matrix-multiply. See Hill p. 216 for an example. // We use rint() to round, rather than just casting. // frobbedVertex.x = (int)rint(Frob.getRow(0).getX()*vertex.x) + (int)rint(Frob.getRow(0).getY()*vertex.y) + (int)rint(Frob.getRow(0).getZ()); frobbedVertex.y = (int)rint(Frob.getRow(1).getX()*vertex.x) + (int)rint(Frob.getRow(1).getY()*vertex.y) + (int)rint(Frob.getRow(1).getZ()); return frobbedVertex; } // // drawTransformedPolygon() // // Given a set of vertices and a TwoDXformMatrix, draw the // transformed polygon // void drawTransformedPolygon(int numVertices, GLintPoint vertices[], TwoDXformMatrix Frob) { GLintPoint frobbedVertices[maxVertices]; for(int i = 0; i < numVertices; i++) { frobbedVertices[i] = transform2D(vertices[i], Frob); } cvs.setColor(1.0, 0.0, 0.0); glBegin(GL_LINE_LOOP); for(int i = 0; i < numVertices; i++) glVertex2i(frobbedVertices[i].x, frobbedVertices[i].y); glEnd(); glFlush(); } // // myMouse() // // Mouse event handler. Capture left-button keypresses up to and until // a right-button event happens; then draw them as a polygon. // // Modify this to also draw the transformed polygon // void myMouse(int button, int state, int x, int y) { static GLintPoint vertices[maxVertices]; static int numVertices = 0; // declare and initialize your transform mtx here: // TwoDXformMatrix Frob ...... if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN)) { printf("vertex entered: %d,%d \n", x, screenHeight - y); fflush(stdout); vertices[numVertices].x = x; vertices[numVertices].y = screenHeight - y; numVertices++; drawBox(x, y); } else if ((button == GLUT_RIGHT_BUTTON) && (state == GLUT_DOWN)) { drawPolygon(numVertices, vertices); numVertices = 0; } } void display(void) { cvs.clearScreen(); cvs.setWindow(0, screenWidth, 0, screenHeight); } int main(void) { cvs.setBackgroundColor(1.0, 1.0, 1.0); glutDisplayFunc(display); glutMouseFunc(myMouse); glutMainLoop(); }