Chris Pollett > Old Classes
> |
HW1 Solutions PageMaya SolutionsThe file name.mp contains the Maya file of my name. Basically, anything that used several of the basic shapes received full credit. As you can see below, my name written with these basic shapes is none too fancy: Code Solutions/****************************************************** * Project: CS116A Homework #1 * File: name.cpp * Purpose: Code to print out my name and the English vowels using openGL * Start date: Sep 25, 2004 * Programmer: Chris Pollett * * Remarks: Letter writing is implemented using a Command Pattern. This would make it * easy to add new letters. * 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 <vector> #include <string> #include <cstring> using namespace std; /* CONSTANTS */ const GLfloat PI = 3.14159265358979323846; /* CLASS DEFINITIONS */ /*-----------------------------------------------*/ class Letter /* PURPOSE: Holds an ascii character together with a string saying how it is drawn. Format of this draw string is as follows: "cmd1 par1 par2 par3 ... cmd2 par1 par2" where everything operation in the string is sepearted by a single space cmdX indicates a command and can be either c (draw arc) or l (draw line) parX indicates a parameter to the command. Ex c .3 .3 .2 0 6.28 l 3 .5 .5 .5 .1 .6 .1 First, draws a complete circle (0 to 6.28 is one revolution) of radius .2 centered at (.3, .3). Then draws a polyline consisting of 3 points (.5,.5), (.5, .1), and (.6, .1). Letter are assumed to fit in a 1.0 x 1.0 pixel box which is scaled by desired size dby the scale parameter of the draw function. */ { private: char _letter; GLfloat _width; string _representation; public: Letter(char l, GLfloat w, string rep); void draw(GLfloat x, GLfloat y, GLfloat scale); GLfloat width() {return _width;} bool canDraw(char l){return (_letter == l); } }; /*-----------------------------------------------*/ class LetterProcessor /* PURPOSE: Is a container for Letters (supporting add) and allows one to print strings of these letters to the screen. */ { private: vector<Letter> _letters; public: void add(Letter l){ _letters.push_back(l);} void draw(GLfloat x, GLfloat y, GLfloat scale, string output); }; /*-----------------------------------------------*/ class ScreenPt /* 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: ScreenPt() { x= y =0; } 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--; } }; /* GLOBALS */ GLsizei winWidth = 600, winHeight = 300; // used for size of window /* 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(ScreenPt pt, ScreenPt& presult, int octant); void lineBres(int x0, int y0, int xEnd, int yEnd); // Polyline functions void polyLineBres(int x[], int y[], int len); void testPolyLineBres(int x[], int y[], int len); void (*g_pfnPolyLine)(int x[], int y[], int len); // Arc functions void circleArcMidpoint(GLint xc, GLint yc, GLint r, GLfloat startTheta, GLfloat endTheta); void testCircleArcMidpoint(GLint xc, GLint yc, GLint r, GLfloat startTheta, GLfloat endTheta); void (*g_pfnCircleArc)(GLint xc, GLint yc, GLint r, GLfloat startTheta, GLfloat endTheta); /* IMPLEMENTATIONS */ /*-----------------------------------------------*/ Letter::Letter(char l, GLfloat w, string rep) /* PURPOSE: constructor RECEIVES: l - the letter to be associated with the draw string w - how wide the letter is. A value from 0.0 to 1.0 rep - string used to say how to draw letter (format described in class def) RETURNS: An instance of letter REMARKS: */ { _letter = l; _width = w; _representation = rep; } /*-----------------------------------------------*/ void Letter::draw(GLfloat x, GLfloat y, GLfloat scale) /* PURPOSE: Used to draw the Letter according to the instructions in _represntation RECEIVES: x and y - coordinates to draw at. scale - how many pixels tall to draw the letter RETURNS: nothing REMARKS: The use of a global function pointer was to aid in testing my code... I first used the built-in OpenGL and GLU line-drawing and arc drawing functions to test to make sure letters were drawing correctly. Once I had this working I wrote my own poly-line and arc drawing functions. Then I switched which was used in draw by changing what the global function pointer pointed to. */ { char *crep = new char[ _representation.length() + 1]; strcpy(crep, _representation.c_str()); char **endH = NULL; char *token = strtok(crep, " "); int len; int *x_arr, *y_arr, xc, yc, radius; GLfloat startTheta, endTheta; while(token) { switch(*token) { case 'l': len = strtol(strtok(NULL, " "), endH, 10); x_arr = new int[len]; y_arr = new int[len]; for(int i = 0; i < len; i++) { x_arr[i] = (int)(x + scale*strtod(strtok(NULL, " "), endH)); y_arr[i] = (int)(y + scale*strtod(strtok(NULL, " "), endH)); } (*g_pfnPolyLine)(x_arr, y_arr, len); glFlush(); delete[] x_arr; // remember to recycle old arrays! delete[] y_arr; break; case 'c': xc = (int)(x + scale*strtod(strtok(NULL, " "), endH)); yc = (int)(y + scale*strtod(strtok(NULL, " "), endH)); radius = (int)(scale*strtod(strtok(NULL, " "), endH)); startTheta = (float)strtod(strtok(NULL, " "), endH); endTheta = (float)strtod(strtok(NULL, " "), endH); (*g_pfnCircleArc)(xc, yc, radius, startTheta, endTheta); break; } token = strtok(NULL, " "); } delete crep; } /*-----------------------------------------------*/ void LetterProcessor::draw(GLfloat x, GLfloat y, GLfloat scale, string output) /* PURPOSE: Draws a string at the specified location and size on the screen RECEIVES: x and y - screen location to draw scale - how many pixels tall output - string to output RETURNS: nothing REMARKS: */ { GLfloat curX = x; GLfloat curY = y; bool drawSucceed; int len = output.size(); //avoid dereferencing more than once int numLetters = _letters.size(); for(int i = 0; i < len ; i++) { drawSucceed = false; for(int j = 0; j < numLetters; j++) { if(_letters[j].canDraw(output[i])) /*linear search.. So fast! Not!! Actually given the small number of letters in most alphabets could be. */ { drawSucceed = true; _letters[j].draw(curX, curY, scale); curX += scale*_letters[j].width(); } } if(!drawSucceed) throw string("Letter: ") + output[i] + " is not drawable."; } } /*-----------------------------------------------*/ 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(ScreenPt p0, ScreenPt 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(ScreenPt pt, ScreenPt& 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 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. Right now it makes too many flag type checks. Could easily be optimized. */ { int dx = abs(xEnd - x0), dy = abs(yEnd - y0); 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; } setPixel(x, y); while(*a < *aEnd) { (*a)++; if(p < 0) { p += twoD; } else { (*b) += slopeSign; p += twoDMinusD; } setPixel(x, y); } } /*-----------------------------------------------*/ 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 testPolyLineBres(int x[], int y[], int len) /* PURPOSE: Draws a polyline using OpenGL's built-in mode 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 */ { glBegin(GL_LINE_STRIP); for(int i = 0; i < len; i++) { glVertex2i(x[i], y[i]); } glEnd(); } /*-----------------------------------------------*/ 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 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. */ ScreenPt pt; ScreenPt startPt, endPt; //start and end points of arc ScreenPt endStartOctantPt, startEndOctantPt; /* corresponding end and start points of these points octants */ ScreenPt 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); GLint p = 1 - r; //Here is where the drawing is actually done while(pt.getx() < pt.gety()) { pt.incx(); if(p < 0) { p += 2 * pt.getx() + 1; } else { pt.decy(); p += 2 * (pt.getx() - pt.gety ()) +1; } //draw points, if applicable, in starting octant reflectOctant(pt, 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(pt, 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(pt, tmpPt,i); setPixel(xc + tmpPt.getx(), yc + tmpPt.gety()); } } } /*-----------------------------------------------*/ void testCircleArcMidpoint(GLint xc, GLint yc, GLint r, GLfloat startTheta, GLfloat endTheta) /* Purpose: Calls GLU function to draw an arc. Used in testing what my letters looked like before writing my own arc drawing function 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. O radians on our unit circle corresponds to the point (0, 1) and we take our angles clockwise around the circle */ { GLUquadricObj *qobj = gluNewQuadric(); gluQuadricDrawStyle(qobj, GLU_LINE); glTranslated(xc, yc, 0); //move origin gluPartialDisk(qobj, r-1, r, 20, 1, toDegrees(startTheta), toDegrees(endTheta - startTheta) ); //draw arc glTranslated(-xc, -yc, 0); //restore origin glFlush(); gluDeleteQuadric(qobj); } /* GLUT AND DRIVER CODE */ /*-----------------------------------------------*/ 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, 400.0, 0.0, 300.0); } /*-----------------------------------------------*/ void drawNameVowels(void) /* PURPOSE: GLUT display callback function for this program. Serves as the main driver for the program. Initializes a LetterProcessor font and adds the neccessary letters to it Finally, draws the desired strings. RECEIVES: nothing RETURNS: nothing REMARKS: Side-effect is to draw my name and the vowels a, e, i, o, u to screen */ { LetterProcessor font; // Add Space font.add(Letter(' ', 1.0, string(""))); // Add Characters... Upper Case font.add(Letter('C', 1, string("c .5 .5 .45 2.14 7.28"))); font.add(Letter('P', .7, string("l 3 .3 .9 .1 .9 .1 .1 l 2 .1 .4 .3 .4 c .3 .65 .25 0 3.14"))); // Add Characters... LowerCase font.add(Letter('a', .6, string("c .3 .3 .2 0 6.28 l 2 .5 .5 .5 .1"))); font.add(Letter('e', .6, string("c .3 .3 .2 3.14 7.85 l 2 .5 .3 .1 .3 l 2 .5 .1 .3 .1"))); font.add(Letter('h', .6, string("l 6 .1 .9 .1 .1 .1 .5 .3 .6 .5 .3 .5 .1"))); //h is being used to check slope >1 and < 1 font.add(Letter('i', .2, string("l 2 .1 .5 .1 .1 c .1 .7 .1 0 6.28"))); font.add(Letter('l', .3, string("l 2 .1 .9 .1 .1"))); font.add(Letter('o', .6, string("c .3 .3 .2 0. 6.28"))); font.add(Letter('r', .6, string("l 4 .1 .6 .1 .1 .1 .5 .5 .5"))); font.add(Letter('s', .6, string("l 2 .4 .5 .5 .5 c .4 .4 .1 3.14 6.28 c .4 .2 .1 0 3.14 l 2 .4 .1 .3 .1 "))); font.add(Letter('t', .6, string("l 2 .3 .9 .3 .1 l 2 .1 .7 .5 .7"))); font.add(Letter('u', .6, string("l 2 .1 .5 .1 .3 c .3 .3 .2 1.57 4.71 l 2 .5 .5 .5 .1"))); // set up draw function pointers to be used //g_pfnPolyLine = testPolyLineBres; // test function which uses built-in OpenGL poly-line g_pfnPolyLine = polyLineBres; // uses my code for poly-line //g_pfnCircleArc = testCircleArcMidpoint; // test function which uses built-in OpenGL arc functions g_pfnCircleArc = circleArcMidpoint;// uses my code for poly-line glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 0.0, 0.0); //here's where we draw the strings to the screen try { font.draw(20.0, 200.0, 60.0, string("Chris Pollett")); font.draw(20.0, 100.0, 60.0, string("aeiou")); } catch(string error) { cerr << error << endl; } 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: */ { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, (GLdouble) newWidth, 0.0, (GLdouble) newHeight); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowPosition(50, 100); glutInitWindowSize(winWidth, winHeight); glutCreateWindow("An Example OpenGL Program"); init(); glutDisplayFunc(drawNameVowels); glutReshapeFunc(winReshapeFn); glutMainLoop(); return 0; } |