Code:
/*
Wheel and Ramp
December 2nd, 2013
To compile:
Linux: g++ -o wheelandramp wheelandramp.cpp -lGL -lGLU -lglut -lm
Mac: g++ -o wheelandramp wheelandramp.cpp -framework Carbon -framework OpenGL -framework GLUT -lm
*/
#ifdef __APPLE__
#include <OpenGL/gl.h> // definitions for GL graphics routines
#include <OpenGL/glu.h> // definitions for GL input device handling
#include <GLUT/glut.h> // deginitions for the GLUT utility toolkit
#else
#include <GL/gl.h> // definitions for GL graphics routines
#include <GL/glu.h> // definitions for GL input device handling
#include <GL/glut.h> // deginitions for the GLUT utility toolkit
#endif
#include <iostream> // definitions for standard I/O routines
#include <cmath> // definitions for math library
using namespace std;
#define WINDOW_WIDTH 800 // window dimensions
#define WINDOW_HEIGHT 600
#define RADIUS 40 // wheel radius
#define PI 3.1415926536
#define CIRC_INC (2 * PI / 30)
#define STARTX (WINDOW_WIDTH)/2
#define STARTY (GROUND_HT)+RADIUS
#define GROUND_HT 100
#define RAMP_STARTX 266
#define NONE 0
#define CREATED 1
#define MOVING 2
#define LOCKED 3
#define ROLLING 4
#define GRAVITY 10
// Global values
float wheel_x;
float wheel_y;
float x_inc;
float y_inc;
float x_speed;
float y_speed;
float ramp_y;
int wheel_status;
static float x = WINDOW_WIDTH / 2;
static float y = WINDOW_HEIGHT - RADIUS;
// Draw a filled circle with center at position (wheel_x, wheel_y) and radius rad
void Circlef(float rad)
{
float theta;
glBegin(GL_POLYGON);
for(theta=0.0; theta < 2 * PI; theta += CIRC_INC)
glVertex2f(wheel_x+rad*cos(theta), wheel_y+rad*sin(theta));
glEnd();
}
void drawWheel(){
//draw the wheel
glColor3f(0, 0, 1);
glPushMatrix();
glTranslatef(wheel_x, wheel_y, 0);
glScalef(RADIUS, RADIUS, 1);
Circlef(32);
glPopMatrix();
}
void drawRamp(){
//draw a triangle for the ramp
glBegin(GL_POLYGON);
glColor3f(0,1,0);
glVertex2f(RAMP_STARTX, GROUND_HT);
glVertex2f(WINDOW_WIDTH, GROUND_HT);
glColor3f(0.2,1,0.4);
glVertex2f(WINDOW_WIDTH, ramp_y);
glEnd();
}
void drawSky(){
// clear window
glClear(GL_COLOR_BUFFER_BIT);
// draw the ground
glPushMatrix();
glBegin(GL_POLYGON);
glColor3f(0.8,1,1);
glVertex2i(0,GROUND_HT);
glColor3f(0.5,0.8,1);
glVertex2i(0,WINDOW_HEIGHT);
glColor3f(0.5,0.8,1);
glVertex2i(WINDOW_WIDTH,WINDOW_HEIGHT);
glColor3f(0.8,1,1);
glVertex2i(WINDOW_WIDTH,GROUND_HT);
glEnd();
glPopMatrix();
}
void drawGround(){
// draw the ground
glPushMatrix();
glBegin(GL_POLYGON);
glColor3f(0,0.6,0);
glVertex2i(0,0);
glColor3f(0,1,0);
glVertex2i(0,GROUND_HT);
glColor3f(0,1,0);
glVertex2i(WINDOW_WIDTH,GROUND_HT);
glColor3f(0,0.6,0);
glVertex2i(WINDOW_WIDTH,0);
glEnd();
glPopMatrix();
}
void drawScene(){
glClear(GL_COLOR_BUFFER_BIT);
drawSky();
drawRamp();
drawGround();
if(wheel_status != NONE){
drawWheel();
}
glutSwapBuffers();
}
void handleMotion(int x, int y){
if(wheel_status == MOVING){
// set wheel's x value (with conditions)
if(x >= STARTX){
wheel_x = x;
}
else{
wheel_x = STARTX;
}
// set wheel's y value (with conditions)
if(y >= GROUND_HT+RADIUS){
wheel_y = y;
}
else{
wheel_y = GROUND_HT+RADIUS;
}
}
glutPostRedisplay();
float dx = wheel_x - RAMP_STARTX;
float dy = wheel_y - GROUND_HT;
float tanalpha = RADIUS / sqrt(dx*dx + dy*dy - RADIUS*RADIUS);
float tantheta = (dy - dx*tanalpha) / (dx + dy*tanalpha);
float sintheta = tantheta / sqrt( 1 + tantheta*tantheta);
float costheta = sqrt( 1 - sintheta*sintheta);
ramp_y = GROUND_HT + tantheta * (WINDOW_WIDTH - RAMP_STARTX);
x_speed = y_speed = 0;
x_inc = GRAVITY * sintheta * costheta;
y_inc = GRAVITY * sintheta * sintheta;
}
void handleRoll(){
//decrement speed
x_speed -= x_inc;
y_speed -= y_inc;
//add new speed to ball position
wheel_x += x_speed;
wheel_y += y_speed;
//check for wheel_y to go below the ground
if(wheel_y < GROUND_HT){
y_speed = 0; //the wheel will not move downward anymore
x_inc = 0; //gravity will not apply anymore
y_inc = 0; //gravity will not apply anymore
wheel_y = GROUND_HT+RADIUS; //make sure the wheel is always on the ground
}
if(wheel_x < 0-RADIUS){
glutIdleFunc(NULL);
wheel_status = NONE;
}
}
//Watch mouse button presses and handle them
void handleButton(int button, int state, int x, int y){
static int wheel_status = NONE;
y = WINDOW_HEIGHT - y; // reverse y so that zero is at the bottom and max value is at the top
if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN){
if(wheel_status == NONE){
wheel_x = STARTX;
wheel_y = STARTY;
ramp_y = GROUND_HT;
wheel_status = CREATED;
}
else{
exit(0);
}
}
else if(button == GLUT_LEFT_BUTTON){
if(state == GLUT_DOWN){
if(wheel_status == CREATED){
wheel_status = MOVING;
// set wheel's x value (with conditions)
if(x >= STARTX){
wheel_x = x;
}
else{
wheel_x = STARTX;
}
// set wheel's y value (with conditions)
if(y >= GROUND_HT+RADIUS){
wheel_y = y;
}
else{
wheel_y = GROUND_HT+RADIUS;
}
}
}
else if(state == GLUT_UP){
wheel_status = LOCKED;
}
else{
exit(0);
}
}
else if(button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN){
if(wheel_status == LOCKED){
glutIdleFunc(handleRoll);
wheel_status = ROLLING;
}
else{
exit(0);
}
}
}
// main program
int main(int argc, char* argv[]){
glutInit(&argc, argv);
// open window and establish coordinate system on it
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutCreateWindow("Wheel and Ramp");
gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT);
// register display and mouse-button callback routines
glutDisplayFunc(drawScene);
glutMouseFunc(handleButton);
glutMotionFunc(handleMotion);
// clear screen to black
glClearColor(0, 0, 0, 1);
glutMainLoop();
return 0;
}