Page 1 of 3

Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 5:54 pm
by Falco Girgis
So I decided to take a CS elective this year... "Introduction to Computer Graphics with OpenGL"--CS424. I thought it would be an easy A while I could concentrate on my other engineering courses.

As usual, I'll be posting completed projects here for your personal entertainment/education. Just be warned, these are actually really lame. =/

The assignment was to construct an icon as an X with a box around it using OpenGL primitives. Then we had to implement a few extremely simple motions (based on glut Timers). The extra credit options included scaling, rotation, and a built-in color swatch for selecting background colors...

Code: Select all

/*
	Falco Girgis
	2/10/11
	CS445
 
	There are two main portions of the program. The first is the class named "Icon" which encapsulates all data for the icon. One
	is instantiated at global scope and manipulated throughout the event handlers.
	The second is the event-handling functions and a few global helper functions. These are where the actual logic manipulating
	the global icon instance is performed.
 
	Extra Credit Attempts:
	1) Icon can also scale (press 'q' or 'a')
	2) Icon can also rotate (press 'w' or 's')
	3) Different colored background for icon
	4) Pop-up menu for changing the canvas background (press 'c' and click on a color)
	
*/
   
#include <GL/glut.h>

/* Note on my_setup.h:
	I had to do my own initialization for the rotation (I needed to rotate about the z axis). I really wanted to attempt
	the extra credit.
 */


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"

#define SCREEN_WIDTH 400
#define SCREEN_HEIGHT 400

//This is a simple vector structure used to group the components of a 2D vector (with simple addition and subtraction operators defined).
struct Vector2 {
	float x, y;
	Vector2(): x(0.0f), y(0.0f) {}
	Vector2(const float p_x, const float p_y): x(p_x), y(p_y) {}
	const Vector2 &operator+= (const Vector2 &rhs) { x += rhs.x; y += rhs.y; return *this; }
	const Vector2 &operator-= (const Vector2 &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
};


/*
	This class encapsulates all data relating to the icon and instantiates in at global scope. The event handler functions set/get/operate
	on this data depending on user-input.
 */
class Icon {
public:
	//enumerated type for the current animation state of the icon.
	enum ACTION { MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT, NONE};
	//enumerated type representing the different potential background colors of the icon
	enum BG_COLOR { RED, BLUE, GREEN, YELLOW, BLACK, PURPLE, LAST_COLOR };
private:
	//current action being performed
	ACTION _currentAction;
	//frame counter for number of frames the current action has executed
	unsigned int _actionCount;
	//current position of the icon
	Vector2 _position;
	//current size of the icon
	Vector2 _scale;
	//current rotation (in radians) of the icon
	float _orientation;
	//array of each vertex in local coordinates (transformed during render to construct actual icon).
	Vector2 _localVertex[8];
	//current color of the icon's background
	BG_COLOR _curColor;
	
public:
	Icon(): _currentAction(NONE), _actionCount(0), _position(200.0f, 200.0f), _scale(50.0f, 50.0f), _orientation(0.0f), _curColor(RED) {
		
		//Initialize line segments
		_localVertex[0] = Vector2(-0.5f, -0.5f);
		_localVertex[1] = Vector2(0.5f, -0.5f);
		_localVertex[2] = Vector2(0.5f, 0.5f);
		_localVertex[3] = Vector2(-0.5f, 0.5f);
		_localVertex[4] = Vector2(0.5f, -0.5f);
		_localVertex[5] = Vector2(-0.5f, 0.5f);
		_localVertex[6] = Vector2(-0.5f, -0.5f);
		_localVertex[7] = Vector2(0.5f, 0.5f);
	
	} 
	
	void Render() {
		
		glLoadIdentity();
		//move the icon into position
		glTranslatef(_position.x, _position.y, 0.0f);
		//resize the icon
		glScalef(_scale.x, _scale.y, 0.0f);
		//reorient the icon
		glRotatef(_orientation, 0.0f, 0.0f, 1.0f);
		
		//determine which background color to use
		switch(_curColor) {
			case RED:
				glColor3f(1.0f, 0.0f, 0.0f);
				break;
			case BLUE:
				glColor3f(0.0f, 0.0f, 1.0f);
				break;
			case GREEN:
				glColor3f(0.0f, 1.0f, 0.0f);
				break;
			case YELLOW:
				glColor3f(1.0f, 1.0f, 0.0f);
				break;
			case BLACK:
				glColor3f(0.0f, 0.0f, 0.0f);
				break;
			case PURPLE:
				glColor3f(1.0f, 0.0f, 1.0f);
				break;
			default:
				break;
		}
		//render background
		glBegin(GL_QUADS);
			glVertex3f(-0.5f, -0.5f, 1.0f);
			glVertex3f(0.5f, -0.5f, 1.0f);
			glVertex3f(0.5f, 0.5f, 1.0f);
			glVertex3f(-0.5f, 0.5f, 1.0f);
		glEnd();
		
		//render line-segments comprising icon
		glColor3f(0, 0, 1.0f);
		glBegin(GL_LINE_LOOP);
			glVertex3f(_localVertex[0].x, _localVertex[0].y, 1.0f);
			glVertex3f(_localVertex[1].x, _localVertex[1].y, 1.0f);
			glVertex3f(_localVertex[2].x, _localVertex[2].y, 1.0f);
			glVertex3f(_localVertex[3].x, _localVertex[3].y, 1.0f);
		glEnd();
		glBegin(GL_LINES);
			glVertex2f(_localVertex[4].x, _localVertex[4].y);
			glVertex2f(_localVertex[5].x, _localVertex[5].y);
		glEnd();
		glBegin(GL_LINES);
			glVertex2f(_localVertex[6].x, _localVertex[6].y);
			glVertex2f(_localVertex[7].x, _localVertex[7].y);
		glEnd();
	}
	
	//tell icon to begin animation
	void BeginAction(ACTION action) {
		_currentAction = action;
		_actionCount = 0;
		
	}
	
	//must be called from timer function to update vertical motion animation
	void UpdateVertMotion() {
		
		switch(_currentAction) {
			case MOVE_UP:
			case MOVE_DOWN:
				_position += (_currentAction == MOVE_UP)? Vector2(0.0f, -2.0f) : Vector2(0.0f, 2.0f);
				++_actionCount;
				if(_actionCount >= 20) {
					_actionCount = 0;
					_currentAction = NONE;
				}
				break;
			default:
				return;
		}
	}
	
	//must be called from the timer function to update horizontal motion animation
	void UpdateHorizMotion() {
		switch(_currentAction) {
			case MOVE_LEFT:
				_position -= Vector2(10.0f, 0.0f);
				++_actionCount;
				if(_actionCount >= 2) {
					_actionCount = 0;
					_currentAction = NONE;
				}
				break;
			default:
				return;
		}
	}
	
	//scales the icon by the supplied vector
	void Scale(const Vector2 &scale) {
		_scale.x *= scale.x;
		_scale.y *= scale.y;
	}
	
	//rotates the icon by the supplied float
	void Rotate(const float delta) {
		_orientation += delta;
	}
	
	//recolors the background image
	void Recolor(BG_COLOR newColor) { _curColor = newColor; printf("NEW COLOR - %d\n", (int)newColor); }
	
} icon;

//global state variable for whether the color-selection swatch is active or not
bool colorSwatchActive = false;

//global functions/event handlers
void init(void);
void display(void);
void reshape(int, int);
void keypress(unsigned char, int, int);
void timer(int);
void mouse(int, int, int, int);

int main(int argc, char **argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); 
	glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT); 
	glutInitWindowPosition(100, 100); 
	glutCreateWindow("CS445 Project #2");
	
	//register event handlers
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keypress);
	glutMouseFunc(mouse);
	glutTimerFunc(1000/20, timer, 1); //1/20th of a second is the minimum update time of the two actions.
	
	init();
	
	glutMainLoop();
	return 0;
}


void init() {
	//initialize viewport and matrix modes
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glClearDepth (1.0f);									
	glDepthFunc (GL_LEQUAL);			
	glEnable (GL_DEPTH_TEST);	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//gluOrtho2D(0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f);
	//needed 3D orthographics project rather than 2D to rotate about z axis.
	glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -100, 10000);
	glMatrixMode(GL_MODELVIEW);
	glClear(GL_COLOR_BUFFER_BIT);
		
	
}

void RenderSwatch() {
	if(!colorSwatchActive) return; //return if the swatch isn't toggled
	
	glLoadIdentity();
	//loop through each potential color
	for(unsigned int i = 0; i < (unsigned int)Icon::LAST_COLOR; ++i) {
		//determine color of current quad
		switch((Icon::BG_COLOR)i) {
			case Icon::RED:
				glColor3f(1.0f, 0.0f, 0.0f);
				break;
			case Icon::BLUE:
				glColor3f(0.0f, 0.0f, 1.0f);
				break;
			case Icon::GREEN:
				glColor3f(0.0f, 1.0f, 0.0f);
				break;
			case Icon::YELLOW:
				glColor3f(1.0f, 1.0f, 0.0f);
				break;
			case Icon::BLACK:
				glColor3f(0.0f, 0.0f, 0.0f);
				break;
			case Icon::PURPLE:
				glColor3f(1.0f, 0.0f, 1.0f);
				break;
			default:
				break;
				
		}
		//render 20x20 quad in swatch
		glBegin(GL_QUADS);
		glVertex3f(0.0f + i*20.0f, SCREEN_HEIGHT-20.0f, 1.0f);
		glVertex3f(20.0f + i*20.0f, SCREEN_HEIGHT-20.0f, 1.0f);
		glVertex3f(20.0f + i*20.0f, SCREEN_HEIGHT, 1.0f);
		glVertex3f(0.0f + i*20.0f, SCREEN_HEIGHT, 1.0f);
		glEnd();	
	}
	
}

void display() {
	glClear(GL_COLOR_BUFFER_BIT); //clear screen
	
	icon.Render(); //render icon
	RenderSwatch(); //render swatch
	
	glFlush(); //request framebuffer flush
}

void reshape(int w, int h) {
	//wasn't a requirement
}

void keypress(unsigned char key, int x, int y) { //exit on any keypress
	
	if(key == ' ') {
		icon.BeginAction(Icon::MOVE_DOWN); //begin downward animation
	}
	else if(key == 'u' || key == 'U') {
		icon.BeginAction(Icon::MOVE_UP); //begin upward animation
	}
	else if(key == 'q' || key == 'Q') {
		icon.Scale(Vector2(1.05f, 1.05f)); //scale the icon larger
	}
	else if(key == 'a' || key == 'A') {
		icon.Scale(Vector2(0.95f, 0.95f)); //scale the icon smaller
	}
	else if(key == 'w' || key == 'W') {
		icon.Rotate(1.2f); //rotate the icon counterclockwise
	}
	else if(key == 's' || key == 'S') {
		icon.Rotate(-1.2f); //rotate the icon clockwise
	}
	else if(key == 'c' || key == 'C') {
		colorSwatchActive = !colorSwatchActive; //toggle colorswatch
	}
	
}

//Event handler registered to catch mouse events.
void mouse(int button, int state, int x, int y) {
	if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		icon.BeginAction(Icon::MOVE_LEFT); //begin left animation
	}
	
	else if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
		exit(0); //quit program
	}
	
	if(!colorSwatchActive) return;
	
	if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { //choose background color
		if(y < 380 || x > 120) return; //not within collision region
		
		icon.Recolor((Icon::BG_COLOR)(x/20)); //calculate enumerated color type based on mouse position
		colorSwatchActive = false; //close swatch
	}
}
	
/*
Note on the timing:
 
I was a little uncertain based on what the handout said about the timing. I just wanted to explain the way I interpretted it.
 
1) U and Space keys cause the icon to begin moving at 20 units over 1 second, where each unit is 2 pixels. That means our timer must
 update at 1sec/20 = .05 seconds.
 
2) The left mouse click causes the icon to move "immediately." The movement is done in 2 sets of 10 pixels each over a total time of
 0.5 seconds. This means that each 10 pixel movement happens in half that time (0.25sec). That is 5 times the timer delay that is 
 required by the vertical motion, so we keep a static "counter" to know when this much time has ellapsed (since you can only have
 one timer callback function).
 
	 
*/
	
void timer(int v) {
	static unsigned int count = 0;
	
	display(); //called every timer event, since that's the most frequently anything can possible animate/change.
	
	++count; 
	icon.UpdateVertMotion();
	
	if(count == 5) {
		icon.UpdateHorizMotion();
		count = 0;
	}
	
	glutTimerFunc(1000/20, timer, 1);
		
}

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 6:19 pm
by N64vSNES

Code: Select all

glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -100, 10000);
Wut?

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 6:28 pm
by pubby8
You should take more advantage of switch fall-through - it would simplify some of the stuff you added.

Also, does your school offer/teach the newer openGL practices? I know this is an introductory course, but it seems a bit silly to be teaching stuff that has become obsolete.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 6:40 pm
by N64vSNES
pubby8 wrote:You should take more advantage of switch fall-through - it would simplify some of the stuff you added.

Also, does your school offer/teach the newer openGL practices? I know this is an introductory course, but it seems a bit silly to be teaching stuff that has become obsolete.
Do you ever have anything positive to say?

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 6:48 pm
by Falco Girgis
Pubby wrote:Also, does your school offer/teach the newer openGL practices? I know this is an introductory course, but it seems a bit silly to be teaching stuff that has become obsolete.
Nope. At least not in the computer science department. And actually, I completely agree. Our teacher isn't even aware of the fact that immediate mode has become deprecated.
Pubby wrote:You should take more advantage of switch fall-through - it would simplify some of the stuff you added.
Y'know, I normally would never push anything like this, considering the fact that I spend almost no time on these kinds of projects, buuuut based on your past history and the fact that I know you're only nitpicking to prove your superiority, I think I will.

I'm going to assume you are referring to these two instances:

Code: Select all

 switch(_curColor) {
         case RED:
            glColor3f(1.0f, 0.0f, 0.0f);
            break;
         case BLUE:
            glColor3f(0.0f, 0.0f, 1.0f);
            break;
         case GREEN:
            glColor3f(0.0f, 1.0f, 0.0f);
            break;
         case YELLOW:
            glColor3f(1.0f, 1.0f, 0.0f);
            break;
         case BLACK:
            glColor3f(0.0f, 0.0f, 0.0f);
            break;
         case PURPLE:
            glColor3f(1.0f, 0.0f, 1.0f);
            break;
         default:
            break;
      }
Ugly as shit? God yes. Could I have done something better? Certainly. I can think of many better approaches including helper functions for converting the enumeration to a color structure/3f or using embedded ternary statements for a single assignment operation. But y'know what? I can't think of how utilizing switch-fall throughs would do any good here without either making it uglier or gaining almost nothing. Put your code where your mouth is and earn your wings, grasshopper.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 6:49 pm
by Falco Girgis
N64vSNES wrote:

Code: Select all

glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -100, 10000);
Wut?
Haha, what? Why you hating?

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 6:54 pm
by Falco Girgis
Not ONLY that, but if you have an even closer look, I DID take advantage of switch fall-throughs in congruence with the ternary operator in a few strategic places:

Code: Select all

 switch(_currentAction) {
         case MOVE_UP:
         case MOVE_DOWN:
            _position += (_currentAction == MOVE_UP)? Vector2(0.0f, -2.0f) : Vector2(0.0f, 2.0f);
            ++_actionCount;
            if(_actionCount >= 20) {
               _actionCount = 0;
               _currentAction = NONE;
            }
            break;
         default:
            return;
      }

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:04 pm
by Arce
You should take more advantage of switch fall-through - it would simplify some of the stuff you added.
What would be simplified, and how? I am legitimately interested.

And, as Falco has nicely pointed out, he DOES take advantage of it here:

Code: Select all

 case MOVE_UP:
         case MOVE_DOWN:
            _position += (_currentAction == MOVE_UP)? Vector2(0.0f, -2.0f) : Vector2(0.0f, 2.0f);
            ++_actionCount;
            if(_actionCount >= 20) {
               _actionCount = 0;
               _currentAction = NONE;
            }
            break;
         default:
            return;
Seems to me like you're just out to bitch, bro.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:07 pm
by pubby8
Pubby wrote:You should take more advantage of switch fall-through - it would simplify some of the stuff you added.
Y'know, I normally would never push anything like this, considering the fact that I spend almost no time on these kinds of projects, buuuut based on your past history and the fact that I know you're only nitpicking to prove your superiority, I think I will.
I'm going to assume you are referring to these two instances:

Code: Select all

 switch(_curColor) {
         case RED:
            glColor3f(1.0f, 0.0f, 0.0f);
            break;
         case BLUE:
            glColor3f(0.0f, 0.0f, 1.0f);
            break;
         case GREEN:
            glColor3f(0.0f, 1.0f, 0.0f);
            break;
         case YELLOW:
            glColor3f(1.0f, 1.0f, 0.0f);
            break;
         case BLACK:
            glColor3f(0.0f, 0.0f, 0.0f);
            break;
         case PURPLE:
            glColor3f(1.0f, 0.0f, 1.0f);
            break;
         default:
            break;
      }
Ugly as shit? God yes. Could I have done something better? Certainly. I can think of many better approaches including helper functions for converting the enumeration to a color structure/3f or using embedded ternary statements for a single assignment operation. But y'know what? I can't think of how utilizing switch-fall throughs would do any good here without either making it uglier or gaining almost nothing. Put your code where your mouth is and earn your wings, grasshopper.
I think a switch statement was best for what you were doing - anything else would get confusing and messy.
The biggest problem with that bit is the default label. You should either leave it out, or make use of it:

Code: Select all

         default:
         case BLACK:
            glColor3f(0.0f, 0.0f, 0.0f);
            break;
      }
This was the part I was talking about:

Code: Select all

  if(key == ' ') {
      icon.BeginAction(Icon::MOVE_DOWN); //begin downward animation
   }
   else if(key == 'u' || key == 'U') {
      icon.BeginAction(Icon::MOVE_UP); //begin upward animation
   }
   else if(key == 'q' || key == 'Q') {
      icon.Scale(Vector2(1.05f, 1.05f)); //scale the icon larger
   }
   else if(key == 'a' || key == 'A') {
      icon.Scale(Vector2(0.95f, 0.95f)); //scale the icon smaller
   }
   else if(key == 'w' || key == 'W') {
      icon.Rotate(1.2f); //rotate the icon counterclockwise
   }
   else if(key == 's' || key == 'S') {
      icon.Rotate(-1.2f); //rotate the icon clockwise
   }
   else if(key == 'c' || key == 'C') {
      colorSwatchActive = !colorSwatchActive; //toggle colorswatch
   }
Which would look cleaner in switch. I dunno, it's just style choice, however you really should avoid "default: break;" - it is bad practice.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:11 pm
by Falco Girgis
Seriously? That's it? Avoid a default switch "just cuz" and use a switch structure for "just cuz I think it's cuter than an if/else structure"?

First of all, if it had actually mattered at all whether or not the default case did anything, I would have not left it empty. As you undoubtedly know, OpenGL isn't going to shit its pants just because I didn't change colors. And a vast amount of the time the default case is erroneous and needs special logic that should NOT fall through to other cases.

Funny story. Your response was not defending your original point which tried to claim that I could have utilized switch-fall throughs better. I stand by my theory that you simply love to bitch at people who DO rather than getting of your ass and DOING yourself. I guarantee you have probably spent over half the time I spent writing this hovering over the thread bitching and psycho analyzing my code.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:20 pm
by Arce
I believe I missed a post somewhere...

edit: My apologies, I did not realize that putting Pubby on my "foes" list prevents me from seeing his posts. Though this is much more pleasing to my browser, I'll go ahead and undo this for the sake of completeness.

edit2: Meh, I'm actually growing tired of high-jacked threads being dedicated to this gentlemen. Back on my blocked list. I advise others to do the same. ;p

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:24 pm
by pubby8
GyroVorbis wrote:Seriously? That's it? Avoid a default switch "just cuz" and use a switch structure for "just cuz I think it's cuter than an if/else structure"?

First of all, if it had actually mattered at all whether or not the default case did anything, I would have not left it empty. As you undoubtedly know, OpenGL isn't going to shit its pants just because I didn't change colors. And a vast amount of the time the default case is erroneous and needs special logic that should NOT fall through to other cases.

Funny story. Your response was not defending your original point which tried to claim that I could have utilized switch-fall throughs better. I stand by my theory that you simply love to bitch at people who DO rather than getting of your ass and DOING yourself. I guarantee you have probably spent over half the time I spent writing this hovering over the thread bitching and psycho analyzing my code.
It actually didn't matter that you wrote comments in your code, but doing so made it higher quality - as would fixing your "default:" bits.
It is like doing this:
try {
functionThatThrows();
}
catch (...) {
//nothing here
}

You'll be in more trouble from catching the exception and not doing anything, than simply avoiding the "try" in the first place.

And why are we arguing over such stupid things? We both admit your code can use more fallthrough, and that it works fine without it - no need to get all emotional on the subject.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:32 pm
by TheBuzzSaw
pubby8 wrote:Which would look cleaner in switch. I dunno, it's just style choice, however you really should avoid "default: break;" - it is bad practice.
According to who? Frankly, I do stuff like that all the time to prevent the compiler from blasting out warnings: "YOU DIDN'T ACCOUNT FOR EVERY POSSIBILITY! WE'RE ALL GONNA DIE!" If you know you haven't hit every option, you really should have the default there, even if it doesn't do anything.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:36 pm
by Falco Girgis
Actually, you're wrong. There's a GCC compiler flag that is enabled by default that flags a warning for not supplying a default case in that switch statement. -Wswitch is enabled with -Wall. And since I'm not supplying a switch case for LAST_COLOR, it would bitch that I am not supplying a case for each potential enumerated value. So it would better to have an empty default case than it would to leave the case out.
Pubby wrote:nd why are we arguing over such stupid things? We both admit your code can use more fallthrough, and that it works fine without it - no need to get all emotional on the subject.
As I seem to recall, you were the one who jumped into MY topic bitching about trivialities.

edit: Damn, TheBuzzSaw got that bitchslap in before I could. Haha.

Re: Falco's (Lame-Ass) OpenGL Projects

Posted: Wed Feb 09, 2011 7:37 pm
by pubby8
TheBuzzSaw wrote:
pubby8 wrote:Which would look cleaner in switch. I dunno, it's just style choice, however you really should avoid "default: break;" - it is bad practice.
According to who? Frankly, I do stuff like that all the time to prevent the compiler from blasting out warnings: "YOU DIDN'T ACCOUNT FOR EVERY POSSIBILITY! WE'RE ALL GONNA DIE!" If you know you haven't hit every option, you really should have the default there, even if it doesn't do anything.
The whole reason compiler complains is because not having a default label is identical to a "default: break;" label!