Chris Pollett > Old Classes >
CS116a

( Print View )

Student Corner:
  [Grades Sec1]

  [Submit Sec1]

  [Email List Sec1]

  [
Lecture Notes]

Course Info:
  [Texts & Links]
  [Topics]
  [Grading]
  [HW Info]
  [Exam Info]
  [Regrades]
  [Honesty]
  [Announcements]

HW Assignments:
  [Hw1]  [Hw2]  [Hw3]
  [Hw4]  [Hw5]

Practice Exams:
  [Mid1]  [Mid2]  [Final]

                           












HW1 Solutions Page

Return to homework page.

Maya Solutions

The 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:

Screenshot of my Maya-ized name

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;
}

Return to homework page.