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.

bitfill.cpp

/******************************************************
* Project:         CS116A Homework #2
* File:              bitfill.cpp          
* Purpose:         Code to print out a letters vaa and ji
*                  three times each as specified in HW2
*                  description
*  
* Start date:      Oct 18, 2004
* Programmer:      Chris Pollett
*
* Remarks:
*
*******************************************************/

#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

//C headers
#include <cmath>

//C++ headers
#include <iostream>
#include <string>

using namespace std;


/*
	Globals
*/
GLsizei winWidth = 400, winHeight = 400; // used for size of window


/*-----------------------------------------------*/
void drawVaa(void)
/*
PURPOSE: Draws the Vaa bitmap three times with only
	     one call to glRasterPos2i
RECEIVES: nothing
RETURNS: nothing
REMARKS: 
*/
{
	GLubyte vaaShape[52] = // vaa symbol byte array
	{   0x00, 0x00,
		0x00, 0xC0, 
		0x03, 0x80, 
		0x0E, 0x00, 
		0x38, 0x00,
		0x71, 0xC0, 
		0x1D, 0x40, 
	    0x07, 0xC0, 
	    0x00, 0x00,
		0x00, 0x00, 
		0x00, 0x80, 
	    0x00, 0x80, 
	    0x00, 0x00
	};

   glColor3f(0.0, 0.0, 0.0);      
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   

   glRasterPos2i(50, 300);

   //Draw bitmap three time, two pixel gap comes from xoffset of 15   
   glBitmap(13, 13, 0.0, 0.0, 15.0, 0.0, vaaShape); 
   glBitmap(13, 13, 0.0, 0.0, 15.0, 0.0, vaaShape);
   glBitmap(13, 13, 0.0, 0.0, 15.0, 0.0, vaaShape);
}

/*-----------------------------------------------*/
void drawJi(void)
/*
PURPOSE: Draws the Ji symbol three times
	as specified in homework description
RECEIVES: nothing
RETURNS: nothing
REMARKS: 
*/
{
   GLuint jiListID = glGenLists(1); // id of ji display list

   // Set up how we draw the points.
    GLfloat widths[2];  // Store supported point size range
	
   // Get supported line size range and step size
   glGetFloatv(GL_LINE_WIDTH_RANGE,widths);

   // Specify the point size -- try to make at least 10   
   GLfloat drawWidth = 10;
   if(drawWidth > widths[1]) drawWidth = widths[1];
		
   //Create display list
   glNewList(jiListID, GL_COMPILE);   
      glLineWidth(drawWidth);    
	  glRecti(50, 100, 60, 150);
	  
	  glBegin(GL_QUADS);
		glVertex2i(50, 150);
		glVertex2i(50, 160);
		glVertex2i(150, 160);
		glVertex2i(150, 150);						
	  glEnd();
	  
	  glLineStipple(1, 0xF0F0);
	  glEnable(GL_LINE_STIPPLE);
	  glBegin(GL_LINE_STRIP);
	     glVertex2i(150, 155);
		 glVertex2i(200, 155);
		 glVertex2i(100, 250);
	  glEnd();
	  glDisable(GL_LINE_STIPPLE);
   glEndList();
   
   //Now draw three times

   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glEnable(GL_BLEND);
   	
	   glColor4f(1.0, 0.0, 0.0, .5);  
	   glCallList(jiListID);
	   
	   glTranslated(50, 0, 0); //used Translate before in my solutions to Hw1
	   
	   glColor4f(0.0, 1.0, 0.0, .5);  
	   glCallList(jiListID);

	   glTranslated(50, 0, 0);

	   glColor4f(0.0, 0.0, 1.0, .5);  
	   glCallList(jiListID);      

	   glTranslated(-100, 0, 0); //undo translations
   glDisable(GL_BLEND);
}

/*-----------------------------------------------*/
void drawVaaJi(void)
/*
PURPOSE: GLUT display callback function for this program.
         Serves as the main driver for the program. 
		 Draws the Vaa bitmap three times using drawVaa then
		 draws the Ji display list three times using drawJi
RECEIVES: nothing
RETURNS: nothing
REMARKS:
*/
{



   glClear(GL_COLOR_BUFFER_BIT);
   glColor3f(0.0, 0.0, 0.0);  
   
   drawVaa();
   drawJi();

   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: Notice resize viewport
*/
{
   winWidth = newWidth;
   winHeight = newHeight;
   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);
   drawVaaJi();

}

/*-----------------------------------------------*/
void init(void)
/*
PURPOSE: Used to initializes our OpenGL window and set up pt pointers for 
                keyboard handler
RECEIVES: Nothing
RETURNS:  Nothing
REMARKS:  Nothing  
*/
{
   glClearColor(1.0, 1.0, 1.0, 0.0);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();   
   gluOrtho2D(0.0, winWidth, 0.0, winHeight); /* note don't set default
       viewport size as will by default be whole window*/
           
   
}

int main(int argc, char** argv)
{
   
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
   glutInitWindowPosition(50, 100);
   glutInitWindowSize(winWidth, winHeight);
   glutCreateWindow("Odd Even Rule Tester");

   init();
   
   //Set up drawing callbacks
   glutDisplayFunc(drawVaaJi);
   glutReshapeFunc(winReshapeFn);

  //Start main loop
   glutMainLoop(); 
   return 0;
}

intext.cpp

/******************************************************
* Project:         CS116A Homework #2
* File:            intext.cpp          
* Purpose:         Code to print out a letter ngii and two points
*                  Points colored according to if both interior/exterior to ngii
* Start date:      Oct 18, 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
*
*          Added keyboard control (arrow keys) to move points around
*          esc switches which point to move
*          space- turns on and off drawing a colored line between two points
*
*******************************************************/

#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

//C headers
#include <cmath>

//C++ headers
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

/*
   CONSTANTS
*/
const GLfloat PI = 3.14159265358979323846; 

/*
   CLASS DEFINITIONS
*/
/*-----------------------------------------------*/
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(GLint newX=0, GLint newY=0)
      {
         x = newX;
		 y = newY;
      }
	  
      void setCoords(GLint x0, GLint y0)
      {
         x = x0;
         y = y0;
      }

      GLint getx() const
      {
         return x;
      }

      GLint gety() const
      {
         return y;
      }
	  
      void incx()
      {
         x++;
      }
      void decx()
      {
         x--;
      }
      void incy()
      {
         y++;
      }	  
      void decy()
      {
         y--;
      }
	  	  
	  bool operator<(const ScreenPt b) const //used by priority_queue in drawing even odd edge;
	  {		
		return x*x + y*y > b.getx()*b.getx() + b.gety()*b.gety();
	  }
	  
};


/*-----------------------------------------------*/
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;
	  void getArcVertices(vector<ScreenPt>& pts, vector<bool>& drawableEdges,
		 GLint xc, GLint yc, GLint radiusX, GLint radiusY, GLfloat startTheta, GLfloat endTheta);
 	  
   public:

      Letter(char l, GLfloat w, string rep);
	  void getVertices(vector<ScreenPt>& pts, vector<bool>& drawableEdges,
		 GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY);
	  void getIntersectEdges(priority_queue<ScreenPt>& pts, ScreenPt p1, ScreenPt p2, 
	     GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY);   
      	  
      void draw(GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY);
      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 scaleX, GLfloat scaleY, string output);
	  
	  void drawIntersectEdges(ScreenPt p1, ScreenPt p2, GLfloat x, GLfloat y, GLfloat scaleX, 
	     GLfloat scaleY, string testString, bool lineModeFlag);
};

/*
   GLOBALS
*/

GLsizei winWidth = 600, winHeight = 600; // used for size of window

GLfloat drawSize = 1; //thickness to draw points 

ScreenPt pt1, pt2, *pactivePt, *ppassivePt; //pointers used by keyboard handlers 
bool lineModeFlag = false; //use to tell if draw a line between two test points

/*
   FUNCTIONS
*/

// Auxiliary functions
void setPixel(GLint x, GLint y);

inline GLint crossProduct(GLint x0, GLint y0, GLint x1, GLint y1);

int intersect(ScreenPt edge1p1, ScreenPt edge1p2, ScreenPt edge2p1, ScreenPt edge2p2, 
	GLint& crossX, GLint& crossY );

bool containedBox(GLint x, GLint y, ScreenPt corner1, ScreenPt corner2);

void orderPtX(ScreenPt& p1, ScreenPt& p2);

void orderPtY(ScreenPt& p1, ScreenPt& p2);

inline GLint sgn(GLint x);

void lineBres(int x0, int y0, int xEnd, int yEnd);

// Polyline functions
void polyLineBres(vector<ScreenPt>& pts, vector<bool>& drawableEdges);

/*
   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::getVertices(vector<ScreenPt>& pts, vector<bool>& drawableEdges,
		 GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY)
/*
PURPOSE: returns all the coordinates positions for drawing the edges of
 the stored letter at the given height. drawableEdges used to control endpoints
 for the various polylines
RECEIVES: pts - vector to store points into
          drawableEdges - vector to store endpoints of polylines
		  x, y -coordinates to draw letter at
		  scaleX, scaleY -width and height of letter.

RETURNS:  
REMARKS:    
*/
{
   istringstream tokens(_representation);
   
   int i, len;
   int tmpX, tmpY, radiusX, radiusY, xc, yc;
   GLfloat tmpF, startTheta, endTheta;
   char token;
   
   while(!tokens.eof())
   {
      tokens >> token;
      switch(token)
      {
         case 'l':
            tokens >> len;

            for(i = 0; i < len; i++)
            {
			  tokens >> tmpF;
              tmpX = (int)(x + scaleX*tmpF);
			  			  
			  tokens >> tmpF;
              tmpY = (int)(y + scaleY*tmpF);
 				
              pts.push_back(ScreenPt(tmpX,tmpY));			  
            }

			drawableEdges.push_back(false);
			for(i = 1; i < len; i++)
				drawableEdges.push_back(true);

            break;

         case 'c':
		 
		    tokens >> tmpF;
			xc = (int)(x + scaleX*tmpF);
			
			tokens >> tmpF;
            yc = (int)(y + scaleY*tmpF);
			
			tokens >> tmpF;
            radiusX = (int)(scaleX*tmpF);
            radiusY = (int)(scaleY*tmpF);
						
            tokens >> startTheta;
            tokens >> endTheta;

			getArcVertices(pts, drawableEdges, xc, yc, radiusX, radiusY, startTheta, endTheta);
            break;
      }
   }
}

/*-----------------------------------------------*/
void Letter::getArcVertices(vector<ScreenPt>& pts, vector<bool>& drawableEdges,
		 GLint xc, GLint yc, GLint radiusX, GLint radiusY, GLfloat startTheta, GLfloat endTheta)
/*
PURPOSE: get vertices of polyline used to draw an ellipse arc
RECEIVES: pts - vector to store points of arc
          drawableEdges - used to say if an adge should be drawn. (one first value false)
		  xc, yc -center of arc
		  radiusX, radiusY - ellipse diameters
          startTheta, endTheta - angle endpoints of arc

RETURNS: as a side effect two vectors pts ad drawableEdges are modified 
REMARKS: SLERP -like use of n angle formulas   
*/
{

   GLint rsTheta = (int)(startTheta/(2*PI));
   GLint reTheta = (int)(endTheta/(2*PI));

   startTheta -= rsTheta*(2*PI);
   endTheta -= reTheta*(2*PI);

   startTheta = (startTheta > 0) ? startTheta : 2*PI - startTheta;
   endTheta = (endTheta > 0) ? endTheta : 2*PI - endTheta;
   endTheta = (endTheta > startTheta) ? endTheta : endTheta + (2*PI);

   GLfloat theta=endTheta - startTheta;
   GLint slices = (radiusX < radiusY) ? radiusX : radiusY;
   if(slices <= 0) slices = 1;
   
   //Calculate start-up sines and cosines
   GLfloat sinsTheta = sin(startTheta), cossTheta = cos(startTheta); 
   GLfloat sincTheta = sin(theta/slices), coscTheta = cos(theta/slices), origCosc = coscTheta;
   GLfloat oldSinc = 0, oldCosc = 1, tmpSinc, tmpCosc;
   GLfloat sinSum, cosSum;
   
   //Use nth angle and sum angle formulas to calculate rest of sines and cosine and vertices
   pts.push_back( ScreenPt((long)(xc + radiusX*sinsTheta), (long)(yc + radiusY*cossTheta)));
   drawableEdges.push_back(false);

   sinSum = sinsTheta * coscTheta + cossTheta*sincTheta;
   cosSum = cossTheta * coscTheta - sinsTheta*sincTheta;

   pts.push_back( ScreenPt((long)(xc + radiusX*sinSum), (long)(yc + radiusY*cosSum)));
   drawableEdges.push_back(true);
	     
   for(int i = 1; i < slices; i++)
   {
      tmpSinc = 2*sincTheta*origCosc - oldSinc;
      tmpCosc = 2*coscTheta*origCosc - oldCosc;
	  
	  oldSinc = sincTheta;
	  oldCosc = coscTheta;
	  
	  sincTheta = tmpSinc;
	  coscTheta = tmpCosc;			  	

      sinSum = sinsTheta * coscTheta + cossTheta*sincTheta;
	  cosSum = cossTheta * coscTheta - sinsTheta*sincTheta;

      pts.push_back( ScreenPt((long)(xc + radiusX*sinSum), (long)(yc + radiusY*cosSum)));
      drawableEdges.push_back(true);

   } 
}

/*-----------------------------------------------*/
void Letter::getIntersectEdges(priority_queue<ScreenPt>& pts, ScreenPt p1, ScreenPt p2, 
	     GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY)
/*
PURPOSE: determines which edges of letter drawn at x and y with given scales
   would intersect the line segment with the endpoints p1 and p2
   Stores the intersection points in a priority queue according to distance from p1.
RECEIVES:
   pts - priority queue to store into
   p1 and p2 - endpoints of line segment
   x and y - point letter is supposed to be at
   scaleX and scaleY - how bug letter is
RETURNS: nothing 
REMARKS: 
*/
{
   vector<ScreenPt> letterPts;
   vector<bool> drawableEdges;
   getVertices(letterPts, drawableEdges, x, y, scaleX, scaleY);
   GLint crossX, crossY, lx, ly;
   int cross;
   
	for(unsigned int i = 0; i < letterPts.size() - 1; i++)
	{
	    lx = letterPts[i].getx();
		ly = letterPts[i].gety();
	    cross =  intersect(p1, p2, letterPts[i], letterPts[i+1], crossX, crossY ); 
		if((drawableEdges[i+1] && cross > 0) || (cross == 0 && crossX == lx && crossY ==
ly)) /* second clause
			is a weak attemp to avoid counting twice if intersect at a corner */
		{
		   pts.push(ScreenPt(crossX - p1.getx(), crossY - p1.gety()));
		}
	} 	
}

/*-----------------------------------------------*/
void Letter::draw(GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY)
/*
PURPOSE: Used to draw the Letter according to the instructions in _represntation
RECEIVES: x and y - coordinates to draw at.
		  scaleX - how many pixels wide to draw the letter
          scaleY - 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.
*/
{
	  vector<ScreenPt> pts;
	  vector<bool> drawableEdges;

	  getVertices(pts, drawableEdges, x, y, scaleX, scaleY);
      polyLineBres(pts, drawableEdges);
	  
}

/*-----------------------------------------------*/
void LetterProcessor::draw(GLfloat x, GLfloat y, GLfloat scaleX, GLfloat scaleY, string output) 
/*
PURPOSE: Draws a string at the specified location and size on the screen
RECEIVES: x and y - screen location to draw
          scaleX - how wide to scale each letter
          scaleY - 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, scaleX, scaleY);
            curX += scaleX*_letters[j].width();
         }
      }

      if(!drawSucceed) throw string("Letter: ") + output[i] + " is not drawable.";
   }
}

/*-----------------------------------------------*/
void LetterProcessor::drawIntersectEdges(ScreenPt p1, ScreenPt p2, GLfloat x, GLfloat y, GLfloat scaleX, 
	     GLfloat scaleY, string testString, bool lineModeFlag)
/*
PURPOSE: draws a line from p1 to p2 colored according to how the line
 intersects with the letters in test string drawn at the given location and scale
RECEIVES:
	p1 and p1 - endpoints of line segment
	x and y - coordinate where testString is supposed to be drawn (note doesn't draw testString)
	scaleX scaleY - scale factors in pixels to multiple letters height and width by
	testString - string to check intersection with
	lineModeFlag -  says whether to draw line or only its endpoints.
RETURNS:  nothing
REMARKS: 		 
*/
{
   GLfloat curX = x;
   GLfloat curY = y;

   priority_queue<ScreenPt> intersectPts;

   int len = testString.size(); //avoid dereferencing more than once
   int numLetters = _letters.size();
   bool drawSucceed;

   for(int i = 0; i < len ; i++)
   {
      drawSucceed = false;

      for(int j = 0; j < numLetters; j++)
      {
         if(_letters[j].canDraw(testString[i])) /*linear search.. So fast! Not!!
                                              Actually given the small number
                                              of letters in most alphabets could be.
											*/
         {
            drawSucceed = true;
            _letters[j].getIntersectEdges(intersectPts, p1, p2, curX, curY, scaleX, scaleY);
            curX += scaleX*_letters[j].width();
         }
      }

      if(!drawSucceed) throw string("Letter: ") + testString[i] + " is not drawable.";
   }	

   GLint lineX = p1.getx(), lineY = p1.gety(), tmpX, tmpY;
   bool isRed = true;
   ScreenPt tmpPt;


   glColor3f(1.0, 0.0, 0.0); 
   glPointSize(drawSize);
   glBegin(GL_POINTS);
      glVertex2i(lineX, lineY);
   glEnd();
   
   glPointSize(1);
   if(intersectPts.empty() && lineModeFlag)
   {
		lineBres(lineX, lineY, p2.getx(), p2.gety());
   }

   
   while(intersectPts.size() > 0)	     
   {   
      tmpPt=intersectPts.top();
	  tmpPt.setCoords(tmpPt.getx()+p1.getx(), tmpPt.gety()+p1.gety());
	  intersectPts.pop();
	  if(lineModeFlag)
	  {
		tmpX = tmpPt.getx();
		tmpY = tmpPt.gety();
		lineBres(lineX, lineY, tmpX, tmpY);
		glFlush();
		
		lineX = tmpX;
		lineY = tmpY;
	  }
	  isRed = (isRed) ? false : true;
	  if(isRed)
	  {
		glColor3f(1.0, 0.0, 0.0);   
	  }
	  else
	  {	  
		glColor3f(0.0, 0.0, 1.0);
	  }
   }
   
   if( lineModeFlag)
   {
       lineBres(lineX, lineY, p2.getx(), p2.gety());   
   }

   glPointSize(drawSize);
   glBegin(GL_POINTS);
      glVertex2i(p2.getx(), p2.gety());
   glEnd();

}

/*-----------------------------------------------*/
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();
}

/*-----------------------------------------------*/
bool containedBox(GLint x, GLint y, ScreenPt corner1, ScreenPt corner2)
/*
PURPOSE: checks if (x,y) is in the rectangle with the given corners
RECEIVES: x, y- coordinates of point to check, 
          corner1, corner2 - points for rectangle 
RETURNS:  true - if is inside
REMARKS: 
*/
{
   GLint x1 = corner1.getx(), y1 = corner1.gety();
   GLint x2 = corner2.getx(), y2 = corner2.gety();
   
   if(((x1 <= x && x2 >= x) || (x1 >= x && x2 <= x) ) &&
	  ((y1 <= y && y2 >= y) || (y1 >= y && y2 <= y)))
      return true;

   return false;	  
}

/*-----------------------------------------------*/
void orderPtX(ScreenPt& p1, ScreenPt& p2)
/*
PURPOSE: arranges two points according to which has smaller x value
RECEIVES: p1 and p2 - points to arrange
RETURNS: nothing 
REMARKS: 
*/
{
	if(p1.getx() > p2.getx())
	{
		ScreenPt tmp;
		tmp = p1;
		p1 = p2;
		p2 = tmp;
	}
}

/*-----------------------------------------------*/
void orderPtY(ScreenPt& p1, ScreenPt& p2)
/*
PURPOSE: arranges two points according to which has smaller y value
RECEIVES:  p1 and p2 - points to arrange
RETURNS: nothing 
REMARKS: 
*/{
	if(p1.gety() > p2.gety())
	{
		ScreenPt tmp;
		tmp = p1;
		p1 = p2;
		p2 = tmp;
	}
}

/*-----------------------------------------------*/
inline GLint sgn(GLint x)
/*
PURPOSE: computes the sign of x 
RECEIVES: number to compute sign of 
RETURNS: sign of the number (i.e., +1 or -1)
REMARKS: 
*/
{
	return (x > 0)? 1 : -1;
}

/*-----------------------------------------------*/
int intersect(ScreenPt edge1p1, ScreenPt edge1p2, ScreenPt edge2p1, ScreenPt edge2p2, 
	GLint& crossX, GLint& crossY )
/*
PURPOSE: checks if two edges intersect 
RECEIVES: endpts of the two edges and two GLint's to store intersection point in
RETURNS: 1 if intersect
         0 if intersect at an endpt
		 -1 if don't intersect 
REMARKS: side effect is crossX and crossY set to intersection point
*/
{
    //1st try to rule out collisions by sorting on x and y coordinates
    orderPtY(edge1p1, edge1p2);
	orderPtY(edge2p1, edge2p2);
	if(edge1p2.gety() < edge2p1.gety() || edge2p2.gety() < edge1p1.gety()) return -1;

    orderPtX(edge1p1, edge1p2);
	orderPtX(edge2p1, edge2p2);	
	if(edge1p2.getx() < edge2p1.getx() || edge2p2.getx() < edge1p1.getx()) return -1;
	if(edge1p2.getx() == edge2p1.getx() && edge1p2.gety() == edge2p1.gety()) //intersect at an end pt
	{
		crossX = edge1p2.getx();
		crossY = edge1p2.gety();
		return 0;
	}

    /*if crosses line crossed edge then turn direction
	  when go from one from one endpt to line to other endpt.
	  we check this cross products
	*/
    GLint e1p1x = edge1p1.getx(), e1p2x = edge1p2.getx();
    GLint e1p1y = edge1p1.gety(), e1p2y = edge1p2.gety();
	
    GLint e2p1x = edge2p1.getx(), e2p2x = edge2p2.getx();
    GLint e2p1y = edge2p1.gety(), e2p2y = edge2p2.gety();
	
	ScreenPt leftE = ScreenPt(e2p1x - e1p1x, e2p1y - e1p1y);
	ScreenPt rightE = ScreenPt(e2p2x - e1p1x, e2p2y - e1p1y);	
		
    ScreenPt edge = ScreenPt(e1p2x - e1p1x, e1p2y - e1p1y);
	ScreenPt edge2 = ScreenPt(e2p2x - e2p1x, e2p2y - e2p1y);

    if(sgn(crossProduct(leftE, edge))*sgn(crossProduct(edge, rightE)) < 0) return -1;
    GLfloat m = (GLfloat)(edge.gety())/edge.getx();
	GLfloat m2 = (GLfloat)(edge2.gety())/edge2.getx();
	
	/*
	  Now find intersection. Check a bunch of special cases (probably could simplify a lot)
	*/
	if(m == m2) // both edges have same slope
	{ 
			crossX = e1p2x;
			crossY = e1p2y;
	}
	else if(edge.getx() == 0) // parallel infinite slope lines
	{
	   crossX = e1p1x;
	   if(edge2.getx() > 0)
	   {
	       crossY = (GLint)(m2*(GLfloat)(crossX - e2p1x) + e2p1y);
		   return 1;
	   }
	   else
	   {
		   if(containedBox(crossX, e2p1y, edge2p1, edge2p2))
		   {
			   crossY = e2p1y;
			   return 1;
		   }
		   if(containedBox(crossX, e2p2y, edge2p1, edge2p2))
		   {
			   crossY = e2p2y;
			   return 1;
		   }
		   return -1;
	   }
	}
	else if(edge2.getx() == 0)
	{
	   crossX = e2p1x;   
	   if(edge.getx() > 0)
	   {
	       crossY = (GLint)(m*(GLfloat)(crossX - e1p1x) +e1p1y);
		   return 1;
	   }
	   else
	   {
		   if(containedBox(crossX, e1p1y, edge1p1, edge1p2))
		   {
			   crossY = e1p1y;
			   return 1;
		   }
		   if(containedBox(crossX, e1p2y, edge1p1, edge1p2))
		   {
			   crossY = e1p2y;
			   return 1;
		   }
		   return -1;
	   }	   
    }
	else
	{
		crossX = (GLint)((e2p1y - e1p1y + m*(GLfloat)e1p1x - m2*(GLfloat)e2p1x)/(m-m2));
		crossY =  (GLint)(m*(GLfloat)(crossX - e1p1x) + e1p1y);	
	}
	
	return 1;
}	




/*-----------------------------------------------*/
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(vector<ScreenPt>& pts, vector<bool>& drawableEdges)
/*
PURPOSE: Draws a polyline using my implmentation of Bresenham's algorithm

RECEIVES: pts - vector of points to draw

RETURNS: nothing

REMARKS: Has side-effect of drawing the poly-line to the screen 
         Assumes that both vectors are of the same length  
*/
{
   int len = pts.size();
   for(int i = 0; i < len - 1; i++)
   {
      if(drawableEdges[i+1])
		 lineBres(pts[i].getx(), pts[i].gety(), pts[i+1].getx(), pts[i+1].gety());
   }
}


/*
   GLUT AND DRIVER CODE
*/


/*-----------------------------------------------*/
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 
		 (In this case only ngii) to it
         Finally, draws the desired strings (This case scaled to whole screen).
RECEIVES: nothing
RETURNS: nothing
REMARKS: Side-effect is to draw ngii to the screen as well as two points
    that are testing if are interior or exterior to the ngii
*/
{
   LetterProcessor font;

   string ngii = "c .16 .7 .125 0 6.28 c .16 .7 .025 0 6.28";
   ngii += " c .34 .525 .125 0 6.28 c .34 .525 .025 0 6.28";
   ngii += " c .7 .6 .2 4.71 7.85 c .7 .6 .1 4.71 7.85";
   ngii += " l 4 .5 .6 .5 .1 .6 .1 .6 .6 l 4 .8 .6 .8 .5 .9 .5 .9 .6";
   ngii += " c .7 .9 .05 0 6.28";
   font.add(Letter('n', 1, ngii));

    // Set up how we draw the points.
	GLfloat sizes[2];  // Store supported point size range
    GLfloat step;     // Store supported point size increments


	// Get supported point size range and step size
	glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
	glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);

	// Specify the point size 

	drawSize = (winWidth < winHeight) ? winWidth/50 : winHeight/50;
	if(drawSize > sizes[1]) drawSize = sizes[1];
    glPointSize(drawSize);


   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(0, 0, winWidth, winHeight, string("n"));
      font.drawIntersectEdges(pt1, pt2, 0, 0, winWidth, winHeight, string("n"), lineModeFlag);
  
    }
   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: Notice resize viewport
*/
{
   winWidth = newWidth;
   winHeight = newHeight;
   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);
   drawNameVowels();

}

/*-----------------------------------------------*/
void keyHandleFn(int key, int x, int y)
/*
PURPOSE: Handles keyboard events. The keyboard was very useful in testing this project
RECEIVES: key - the special key (non a-z letter) pressed
          x - screen X position
		  y - screen Y position
RETURNS: nothing
REMARKS:    
*/
{
	ScreenPt *tmp;
	
	switch(key)
	{
		case 27: //escape key
			tmp = pactivePt;
			pactivePt = ppassivePt;
			ppassivePt = tmp;
			break;
		
	    case ' ':
			lineModeFlag = (lineModeFlag)? false : true;
			break;
	
		case GLUT_KEY_LEFT:
			pactivePt->decx();  
			break;	
		
		case GLUT_KEY_RIGHT:
			pactivePt->incx();		
			break;		
				
		case GLUT_KEY_UP:
			pactivePt->incy();		
			break;		
				
		case GLUT_KEY_DOWN:
			pactivePt->decy();			
			break;				
	}
	
	
	glutPostRedisplay();
}

/*-----------------------------------------------*/
void keyHandleFn1(unsigned char key, int x, int y)
/*
PURPOSE: Handles keyboard events like space and esc. 
       The keyboard was very useful in testing this project
RECEIVES: key - the special key (non a-z letter) pressed
          x - screen X position
		  y - screen Y position
RETURNS: nothing
REMARKS:    
*/
{
	ScreenPt *tmp;
	
	switch(key)
	{
		case 27: //escape key
			tmp = pactivePt;
			pactivePt = ppassivePt;
			ppassivePt = tmp;
			break;
		
	    case ' ':
			lineModeFlag = (lineModeFlag)? false : true;
			break;
	}
	
	
	glutPostRedisplay();
}

/*-----------------------------------------------*/
void readParameters()
/*
PURPOSE: Reads Parameter file with initial window size and two points position
RECEIVES: nothing
RETURNS: nothing
REMARKS:    
*/
{
   ifstream fin;
   int tmpX1, tmpY1, tmpX2, tmpY2;
   
   try
   {
      fin.open("parameters.txt");
	  if(fin.fail()) throw string("Failed to open parameters.txt");
	  
	  if(!(fin >> winWidth >> winHeight >> tmpX1 >> tmpY1 >> tmpX2>> tmpY2))
	  {
	    fin.close();
		throw string("parameters.txt format incorrect");
	  }
	  
	  fin.close();
   }
   catch(string error)
   {
	  cerr << error << endl;
      cerr << "Using default values" << endl;
	  
	  tmpX1 = 0;
	  tmpY1 = 0;
	  tmpX2 = winWidth;
	  tmpY2 = winHeight;	
   }
   
   pt1.setCoords(tmpX1, tmpY1);
   pt2.setCoords(tmpX2, tmpY2);
}


/*-----------------------------------------------*/
void init(void)
/*
PURPOSE: Used to initializes our OpenGL window and set up pt pointers for 
		keyboard handler
RECEIVES: Nothing
RETURNS:  Nothing
REMARKS:  Nothing  
*/
{
   glClearColor(1.0, 1.0, 1.0, 0.0);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();   
   gluOrtho2D(0.0, winWidth, 0.0, winHeight); /* note don't set default
       viewport size as will by default be whole window*/
	   
   pactivePt = &pt1;
   ppassivePt = &pt2;
   
}

int main(int argc, char** argv)
{
   readParameters();
   
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
   glutInitWindowPosition(50, 100);
   glutInitWindowSize(winWidth, winHeight);
   glutCreateWindow("Odd Even Rule Tester");

   init();
   
   //Set up drawing callbacks
   glutDisplayFunc(drawNameVowels);
   glutReshapeFunc(winReshapeFn);
   
   //Set up keyboard handlers
   glutSpecialFunc(keyHandleFn); // for arrow keys   
   glutKeyboardFunc(keyHandleFn1); // for space esc   
   
   //Start main loop
   glutMainLoop(); 
   return 0;
}

Return to homework page.