Chris Pollett > Old Classes > CS216
( Print View )

Student Corner:
  [Grades Sec1]

  [Submit Sec1]

  [Class Sign Up Sec1]

  [
Lecture Notes]
  [Discussion Board]

Course Info:
  [Texts & Links]
  [Topics/Outcomes]
  [Outcomes Matrix]
  [Grading]
  [HW/Quiz Info]
  [Exam Info]
  [Regrades]
  [Honesty]
  [Additional Policies]
  [Announcements]

HWs and Quizzes:
  [Hw1]  [Hw2]  [Hw3]
  [Hw4]  [Quizzes]  [Project]

Practice Exams:
  [Mid]  [Final]

                           












HW1 Solutions Page

To create a solution set, I select problem solutions from the one or more people who I thought did well on the assignment. For allowing me to use their homework, the student receives 1 bonus point added after curving. If you see your homework used, but do not want me to use it, just ask, and I will choose someone else's homework. They, rather than you, will receive the bonus point. Below are the results of this process for this homework:

[Hw1 Book Problems-PDF]

/******************************************************
 * Copyright (c):   2010, All Rights Reserved.
 * Project:         CS 216 Homework #1
 * File:            bezier.cpp
 * Purpose:         Code to display an ampersand with piecewise Bezier curves
 * Start date:      Feb. 1, 2010
 * Programmer:      SJSU Students
 *
 * Remarks: Adapted from Hearn and Baker (3rd edition), and sample code by Chris Pollett
 *
 *******************************************************/

#ifdef WIN32
#include <GL/glut.h>
#endif

#ifdef __APPLE__ //for MAC
/*
 I created my project under xcode. I chose new C++ tool as the kind of project.
 Then 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>

using namespace std;

/*
 CONSTANT DEFINITIONS
 */

/*
 PURPOSE: Defines the distance tolerance at which we consider a Bezier curve suitably
 flat to approximate with a straight line.
 */
static const GLfloat DELTA = 0.125f;

/*
 PURPOSE: Cubic Bezier control points for one of the ampersand's outline curves.
 */
static const GLfloat AMPERSAND_OUTSIDE[][2] =
{
	{ -19.9360341151, 22.4989339019 },
	{ -18.3487325278, 24.3468372424 },
	{ -16.7614309405, 26.1947405828 },
	{ -15.1741293532, 28.0426439232 },
	{ -8.90314236541, 34.9991949531 },
	{ 6.67208230173, 26.8476280782 },
	{ -6.21890547264, 14.3255152807 },
	{ -8.31891442991, 12.6429598547 },
	{ -9.77256574271, 11.6958066809 },
	{ -11.5493958778, 10.6297085999 },
	{ -8.42034212803, 5.25699909057 },
	{ -2.19215193353, -1.32648292160 },
	{ 1.74129353234, -4.43781094527 },
	{ 4.54890269552, 0.108079056707 },
	{ 4.12486450880, 3.07898350998 },
	{ 2.53978948012, 6.49500076024 },
	{ 0.667715068026, 6.49695493185 },
	{ -1.20435934406, 6.49890910346 },
	{ -3.07643375615, 6.50086327508 },
	{ -3.18937877060, 6.63892447103 },
	{ -3.27932632880, 6.78283446451 },
	{ -3.36927388699, 6.92674445800 },
	{ -2.70491940943, 8.23754432849 },
	{ -2.04056493186, 9.54834419898 },
	{ -1.37621045430, 10.8591440695 },
	{ 6.00065445864, 10.6381948902 },
	{ 11.6766444773, 10.4722399786 },
	{ 18.7899146043, 11.2605726807 },
	{ 18.8903162811, 11.1695146501 },
	{ 18.9907179578, 11.0784566195 },
	{ 19.0911196345, 10.9873985889 },
	{ 18.3782414633, 9.22012076697 },
	{ 18.0820999982, 7.64964012121 },
	{ 17.9857838607, 6.31604946882 },
	{ 17.8938962268, 6.21784698422 },
	{ 17.8020085930, 6.11964449961 },
	{ 17.7101209591, 6.02144201500 },
	{ 14.6532243074, 6.15263780224 },
	{ 11.5963276557, 6.28383358948 },
	{ 8.53943100398, 6.41502937672 },
	{ 9.69758383955, 1.38588268261 },
	{ 7.91336103071, -1.72979659189 },
	{ 4.64264787888, -6.16259847137 },
	{ 10.3072016784, -9.77575642440 },
	{ 15.2570244572, -9.35726420743 },
	{ 16.0996396540, -8.93670408038 },
	{ 17.1332640391, -8.25407316389 },
	{ 18.1668884242, -7.57144224740 },
	{ 19.2005128093, -6.88881133091 },
	{ 19.3329361189, -6.95773885322 },
	{ 19.4653594285, -7.02666637554 },
	{ 19.5977827381, -7.09559389785 },
	{ 19.8147548067, -7.57523426944 },
	{ 20.0317268753, -8.05487464102 },
	{ 20.2486989439, -8.53451501261 },
	{ 20.2231038892, -8.69158549451 },
	{ 20.1975088345, -8.84865597640 },
	{ 20.1719137797, -9.00572645830 },
	{ 18.2774292675, -11.1286645809 },
	{ 16.3829447553, -13.2516027034 },
	{ 14.4884602431, -15.3745408260 },
	{ 10.1388539908, -16.3871711123 },
	{ 5.36193069383, -16.1860047836 },
	{ -1.05619136233, -11.2916175200 },
	{ -21.3067072164, -26.6326323260 },
	{ -42.5645563154, -1.34013116857 },
	{ -18.9057200352, 9.75116978934 },
	{ -21.1487096596, 14.7301572544 },
	{ -21.8947244451, 20.1298158036 },
	{ -19.9360341151, 22.4989339019 },
};

/*
 PURPOSE: Cubic Bezier control points for one of the ampersand's interior curves.
 */
static const GLfloat AMPERSAND_INTERIOR0[][2] =
{
	{ -17.6703348129, 7.12155735631 },
	{ -14.0845265233, 0.820818776667 },
	{ -8.94379092306, -5.01502374745 },
	{ -3.75579385895, -9.34995166686 },
	{ -9.60743224915, -12.9503244153 },
	{ -14.7570742329, -11.1880004932 },
	{ -17.9296647260, -7.65424577855 },
	{ -22.0036548322, -3.11647710759 },
	{ -22.8175527345, 4.34232206983 },
	{ -17.6714638857, 7.11774369311 },
};

/*
 PURPOSE: Cubic Bezier control points for one of the ampersand's interior curves.
 */
static const GLfloat AMPERSAND_INTERIOR1[][2] =
{
	{ -12.9920451435, 13.3902863398 },
	{ -22.5759329559, 35.0182365443 },
	{ 4.57594874351, 25.5419988809 },
	{ -12.9410488052, 13.3386021868 },
};

/*
 CLASS DEFINITIONS
 */

/*-----------------------------------------------*/
class Point2f
/*
 PURPOSE: Encapsulates the notions of a point (x,y) on the screen
 */
{
public:
	Point2f();
	Point2f( GLfloat ix, GLfloat iy );
	
	inline GLfloat getX() const;
	inline GLfloat getY() const;
	
	void set( GLfloat ix, GLfloat iy );
	
	Point2f operator * ( GLfloat alpha ) const;
	Point2f operator + ( const Point2f & b ) const;
	Point2f operator - ( const Point2f & b ) const;
	
	void lerp( const Point2f & a, const Point2f & b, GLfloat alpha );
	GLfloat distToLine( const Point2f & a, const Point2f & b ) const;
	GLfloat length() const;
	
private:
	GLfloat x, y;
};

/*-----------------------------------------------*/
Point2f::Point2f()
/*
 PURPOSE: Default constructor, performing no initialization.
 REMARKS: This constructor does not initialize the data, so use with caution.
 */
{
}

/*-----------------------------------------------*/
Point2f::Point2f( GLfloat ix, GLfloat iy ) : x( ix ), y( iy )
/*
 PURPOSE: Initialize a point with given coordinates.
 RECEIVES: Coordinates.
 */
{
}

/*-----------------------------------------------*/
inline GLfloat Point2f::getX() const
/*
 PURPOSE: Retrieve the X-coordinate.
 RETURNS: The X-coordinate.
 */
{
	return x;
}

/*-----------------------------------------------*/
inline GLfloat Point2f::getY() const
/*
 PURPOSE: Retrieve the Y-coordinate.
 RETURNS: The Y-coordinate.
 */
{
	return y;
}

/*-----------------------------------------------*/
void Point2f::set( GLfloat ix, GLfloat iy )
/*
 PURPOSE: Initialize a point with given coordinates.
 RECEIVES: Coordinates.
 */
{
	x = ix;
	y = iy;
}


/*-----------------------------------------------*/
inline Point2f Point2f::operator * ( GLfloat alpha ) const
/*
 PURPOSE: Scale a point by a scalar.
 RECEIVES: Scale factor.
 REMARKS: Side effect is scaling the coordinates of this point by the given scale factor.
 */
{
	return Point2f( x * alpha, y * alpha );
}

/*-----------------------------------------------*/
inline Point2f Point2f::operator + ( const Point2f & b ) const
/*
 PURPOSE: Sums two points.
 RECEIVES: The right-hand side of the addition expression.
 RETURNS: Sum of this point and the addend
 */
{
	return Point2f( x + b.x, y + b.y );
}

/*-----------------------------------------------*/
inline Point2f Point2f::operator -( const Point2f & b ) const
/*
 PURPOSE: Subtracts two points.
 RECEIVES: The right-hand side of the subtraction expression.
 RETURNS: Difference of this point and the right-hand side point
 */
{
	return Point2f( x - b.x, y - b.y );
}

/*-----------------------------------------------*/
void Point2f::lerp( const Point2f & a, const Point2f & b, GLfloat alpha )
/*
 PURPOSE: Linearly interpolate between two points.
 RECEIVES: Two points and a weight for the second point.
 REMARKS: Side effect is modifying this point to contain the interpolated position.
 */
{
	Point2f v = b - a;
	Point2f u = *this - a;
	*this = a * ( 1 - alpha ) + b * alpha;
}

/*-----------------------------------------------*/
GLfloat Point2f::distToLine( const Point2f & a, const Point2f & b ) const
/*
 PURPOSE: Calculate distance from a point to a line.
 RECEIVES: Two distinct points on the line.
 RETURNS: The distance of this point to two line formed by the two given points.
 */
{
	Point2f v = b - a;
	Point2f u = *this - a;
	return fabs( u.x * v.y - u.y * v.x ) / v.length();
}

/*-----------------------------------------------*/
GLfloat Point2f::length() const
/*
 PURPOSE: Calculate length of vector from origin to the point.
 RETURNS: The distance of this point to the origin.
 */
{
	return static_cast<GLfloat>( sqrt( x * x + y * y ) );
}

/*
 FUNCTIONS
 */

/*-----------------------------------------------*/
void drawBezierRS( const Point2f & p0, const Point2f & p1, const Point2f & p2, const Point2f & p3 )
/*
 PURPOSE: tessellate a Bezier curve using de Casteljeau's method to perform 
 recursive subdivision and send points to OpenGL.
 RECEIVES: The four Bezier curve control points.
 REMARKS: Side effect is to send vertex data to OpenGL
 */
{
	Point2f r0;
	Point2f r1;
	Point2f r2;
	Point2f s0;
	Point2f s1;
	Point2f t0;
	
	r0.lerp( p0, p1, 0.5f );
	r1.lerp( p1, p2, 0.5f );
	r2.lerp( p2, p3, 0.5f );
	
	s0.lerp( r0, r1, 0.5f );
	s1.lerp( r1, r2, 0.5f );
	
	t0.lerp( s0, s1, 0.5f );
	
	if ( p1.distToLine( p0, p3 ) >= DELTA || p2.distToLine( p0, p3 ) >= DELTA )
	{
		// first generate all vertices (and line segments) up to the midpoint
		drawBezierRS( p0, r0, s0, t0 );
		
		// emit the midpoint
		glVertex2f( t0.getX(), t0.getY() );
		
		// generate vertices (and line segments) up to the end point
		drawBezierRS( t0, s1, r2, p3 );
	}
	else
		// emit the end point since we've already emitted midpoints leading up to this
		glVertex2f( p3.getX(), p3.getY() );
}

/*-----------------------------------------------*/
void drawBezierSegment( const Point2f & p0, const Point2f & p1, const Point2f & p2, const Point2f & p3 )
/*
 PURPOSE: tessellate a Bezier curve using recursive subdivision and send points to OpenGL.
 RECEIVES: The four Bezier curve control points.
 REMARKS: Side effect is to send vertex data to OpenGL
 */
{
	// We draw the initial point here to simplify the recursive code to generate only
	// midpoints and end points
	glVertex2f( p0.getX(), p0.getY() );
	drawBezierRS( p0, p1, p2, p3 );
}

/*-----------------------------------------------*/
static void drawPiecewiseBezier( const GLfloat data[][2], int numPoints )
/*
 PURPOSE: render a set of piecewise Bezier curve segments using recursive subdivision and send points to OpenGL.
 RECEIVES: An array of piece wise Bezier curves. The point array contains a vertex followed by two control points 
 and an end point. The end point of one segment defines the start of the next segment. The numPoints
 input is the total number of vertices, which must be three times the number of segments plus one 
 final end point.
 REMARKS: Side effect is to send vertex data to OpenGL
 */
{
	const int NUM_CUBIC_BEZIER_CONTROL_POINTS = 4;
	Point2f ctrlPts[ NUM_CUBIC_BEZIER_CONTROL_POINTS ];
	for ( int a_nPieces = 0; a_nPieces < numPoints - 1; a_nPieces += NUM_CUBIC_BEZIER_CONTROL_POINTS - 1 )
	{
		for ( int i = 0; i < NUM_CUBIC_BEZIER_CONTROL_POINTS; ++i )
			ctrlPts[ i ].set( data[ a_nPieces + i ][ 0 ], data[ a_nPieces + i ][ 1 ] );
		
		drawBezierSegment( ctrlPts[ 0 ], ctrlPts[ 1 ], ctrlPts[ 2 ], ctrlPts[ 3 ] );
	}
}

/*-----------------------------------------------*/
void drawAmpersand(void)
/*
 PURPOSE: GLUT display callback function for this program.
 Draw an ampersand using piecewise cubic Bezier curves.
 */
{
	const GLfloat RED_COLOR[ 3 ] = { 1.0f, 0.0f, 0.0f };
	glColor3fv( RED_COLOR );
	
	glBegin(GL_LINE_STRIP);
	drawPiecewiseBezier( AMPERSAND_OUTSIDE, sizeof( AMPERSAND_OUTSIDE ) / sizeof( AMPERSAND_OUTSIDE[ 0 ] ) );
	glEnd();
	
	glBegin(GL_LINE_STRIP);
	drawPiecewiseBezier( AMPERSAND_INTERIOR0, sizeof( AMPERSAND_INTERIOR0 ) / sizeof( AMPERSAND_INTERIOR0[ 0 ] ) );
	glEnd();
	
	glBegin(GL_LINE_STRIP);
	drawPiecewiseBezier( AMPERSAND_INTERIOR1, sizeof( AMPERSAND_INTERIOR1 ) / sizeof( AMPERSAND_INTERIOR1[ 0 ] ) );
	glEnd();
}


/*
 GLUT AND DRIVER CODE
 */

/*-----------------------------------------------*/
void init(void)
/*
 PURPOSE: Used to initializes our OpenGL window
 */
{
	glClearColor(1.0, 1.0, 1.0, 0.0);
}

/*-----------------------------------------------*/
void drawFn(void)
/*
 PURPOSE: GLUT display callback function for this program.
 Draw an ampersand using piecewise cubic Bezier curves.
 */
{
	glClear(GL_COLOR_BUFFER_BIT);
	
	drawAmpersand();
	
	glFlush();
}

/*-----------------------------------------------*/
void winReshapeFn(int newWidth, int newHeight)
/*
 PURPOSE: Resizes/redisplays the contents of the current window
 RECEIVES: newWidth, newHeight -- the new dimensions (ignored)
 REMARKS: Selects an orthographic projection surrounding the ampersand.
 */
{   
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	const int AMPERSAND_VIEW_LEFT_BOUND = -30;
	const int AMPERSAND_VIEW_RIGHT_BOUND = 30;
	const int AMPERSAND_VIEW_BOTTOM_BOUND = -20;
	const int AMPERSAND_VIEW_TOP_BOUND = 40;
	gluOrtho2D(AMPERSAND_VIEW_LEFT_BOUND, AMPERSAND_VIEW_RIGHT_BOUND, 
			   AMPERSAND_VIEW_BOTTOM_BOUND, AMPERSAND_VIEW_TOP_BOUND);
	
	glClear(GL_COLOR_BUFFER_BIT);
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	
	const int WINDOW_INITIAL_POSITION_X = 50;
	const int WINDOW_INITIAL_POSITION_Y = WINDOW_INITIAL_POSITION_X;
	const int WINDOW_INITIAL_SIZE_X = 500;
	const int WINDOW_INITIAL_SIZE_Y = WINDOW_INITIAL_SIZE_X;
	glutInitWindowPosition(WINDOW_INITIAL_POSITION_X, WINDOW_INITIAL_POSITION_Y);
	glutInitWindowSize(WINDOW_INITIAL_SIZE_X, WINDOW_INITIAL_SIZE_Y);
	glutCreateWindow("Recursive Subdivision of Bezier Curve");
	
	init();
	glutDisplayFunc(drawFn);
	glutReshapeFunc(winReshapeFn);
	glutMainLoop();
	return 0;
}