/* Tutor on Bezier curves (flat) Written by Caroline Geiersbach and Scott D. Anderson scott.anderson@acm.org Summer 2003 */ #include #include //colors used in program twTriple plainText = {1,1,1}; // white twTriple headerText = {0,1,1}; // cyan twTriple infoText = {1,0,0}; // red //window stuff GLuint window, worldWindow, commandWindow; const int GAP = 25; //gap between subwindows const int cWinWidth = 450; //command window width const int cWinHeight = 550; //command window height const int wWinWidth = 450; //world window width const int wWinHeight = 550; //world window height const int mWinWidth = cWinWidth+wWinWidth+GAP*3; //main window width const int mWinHeight = 600; //main window height // for picking a control point with the mouse const int NO_POINT = -1; int selectedPoint = NO_POINT; // a mouse-selected point static int numPoints; void redisplayAll(void) { glutSetWindow(worldWindow); glutPostRedisplay(); glutSetWindow(commandWindow); glutPostRedisplay(); } const int MAX_POINTS = 15; //specify all control points here GLfloat points[MAX_POINTS][3]; // Have some initial values. GLfloat originalPoints[MAX_POINTS][3] = { {-7,-3,1}, {-6,-3,1}, {-5,0,1}, {-4,-5,1}, {-3,5,1}, {-2,-3,1}, {-1,3,1}, {0,-16,1}, {1,0,1}, {2,-3,1}, {3,-10,1}, {4,13,1}, {5,-10,1}, {6,-3,1},{7,-4,1}, }; //cells are used in the command window; each cell has an id; a //raster location at x,y; min and max values; a current value; the //step for adjusting values; info on what the cell specifies; and //finally the format of the printed values. typedef struct _cell { int id; int x, y; GLfloat min, max; GLfloat value; GLfloat step; char* info; char* format; } cell; cell pickedPoint[3] = { { 1, 180, 260, -24.0, 24.0, 0.0, 0.05, "Specifies X coordinate of point that was last clicked.", "%.2f" }, { 2, 240, 260, -24.0, 24.0, 0.0, 0.05, "Specifies Y coordinate of point that was last clicked.", "%.2f" }, { 3, 300, 260, -24.0, 24.0, 0.0, 0.0, //z coord non-adjustable "Specifies Z coordinate of point that was last clicked.", "%.2f" }, }; cell curveParams[1] = { {5, 225, 150, 0, MAX_POINTS, 4, 1, "Specifies the number of control points for the curve.", "%.0f"}, }; GLint selection = 0; int old_y; //for command mouse //moves information from command window to corresponding global variables void setVals () { numPoints = (int)curveParams[0].value; } void drawBezier(GLfloat* cp) { int steps = 100; glMap1f(GL_MAP1_VERTEX_3, 0, 1, 3, numPoints, cp); glEnable(GL_MAP1_VERTEX_3); glMapGrid1f(steps,0.0,1.0); glEvalMesh1(GL_LINE,0,steps); } void mainDisplay() { //set up camera glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,mWinWidth,mWinHeight,0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.7, 0.7, 1.0, 0.0); //light blue background glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(0,0,0); twSetFont("helvetica", 12); twDrawString(GAP, 20, "your Bezier curve"); twDrawString(GAP*2+wWinWidth, 20, "adjustment window"); glutSwapBuffers(); } /* WORLD */ void worldDisplay(void) { int i,j; glClearColor(1,1,1,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); twCamera(); twColorName(TW_ORANGE); glLineWidth(3); drawBezier(&points[0][0]); twColorName(TW_BLACK); glLineWidth(1); glBegin(GL_LINE_STRIP); //lines between points for(i=0;iscreen[0]-4 && yscreen[1]-4) { return i; } } return NO_POINT; } void adjust(int x, int y) { if(selectedPoint != NO_POINT) { twTriple world; //new coords for element twTriple A,B,V; twTriple winA = {x,y,0}; twTriple winB = {x,y,1}; twUnProject(A,winA); twUnProject(B,winB); twVector(V,B,A); twPointOnLine(points[selectedPoint],A,V,0.5); } } static int mouse_x, mouse_y; void worldMouse(int button, int state, int x, int y) { y=wWinHeight-y; if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN){ mouse_x=x; mouse_y=y; selectedPoint = pick(x,y); } redisplayAll(); } void worldMotion(int x, int y) { y=wWinHeight-y; adjust(x,y); redisplayAll(); } void worldInit() { glutMouseFunc(worldMouse); glutMotionFunc(worldMotion); setVals(); } /* COMMAND */ void cellDraw(cell* cell) { if (selection == cell->id) { glColor3fv(infoText); twDrawString(10, 20, cell->info); twDrawString(cell->x, cell->y, cell->format, cell->value); } else { glColor3fv(plainText); twDrawString(cell->x, cell->y, cell->format, cell->value); } } //returns cell id if cell has been clicked on; 0 otherwise int cellHit(cell* cell, int x, int y) { if (x > cell->x && x < cell->x+55 && y > cell->y-15 && y < cell->y+15){ return cell->id; } return 0; } //updates the cell's value void cellUpdate(cell* cell, int update) { if (selection != cell->id) return; cell->value += update * cell->step; //tests for min and max values of the points if (cell->value < cell->min) cell->value = cell->min; else if (cell->value > cell->max) cell->value = cell->max; setVals(); } //display on right side; where user can adjust values void commandDisplay() { //set up camera glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, cWinWidth, cWinHeight, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.0, 0.0, 0.0, 1.0); //clear to black glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); twSetFont("helvetica", 18); glColor3fv(plainText); twDrawString(10, curveParams[0].y, "number of control points:"); cellDraw(&curveParams[0]); if(selectedPoint != NO_POINT) { //point has been selected int p = selectedPoint; pickedPoint[0].value = points[p][0]; pickedPoint[1].value = points[p][1]; pickedPoint[2].value = points[p][2]; twDrawString(10, pickedPoint[0].y, "point coordinates = ("); twDrawString(pickedPoint[0].x+55, pickedPoint[0].y, ","); twDrawString(pickedPoint[1].x+55, pickedPoint[1].y, ","); twDrawString(pickedPoint[2].x+55, pickedPoint[2].y, ")"); cellDraw(&pickedPoint[0]); cellDraw(&pickedPoint[1]); cellDraw(&pickedPoint[2]); } else { twColorName(TW_ORANGE); twDrawString(10, pickedPoint[0].y, "click on point to view its coords."); } if (!selection) { twColorName(TW_CYAN); twDrawString(10, 20, "Click on args and move mouse to modify values."); twDrawString(10, 50, "(z can't be modified)"); } //directions at bottom of command window twColorName(TW_CYAN); twDrawString(10,425, "Hit

to print point values."); twDrawString(10,465, "Hit to reset points to original values."); glutSwapBuffers(); } void commandMouse(int button, int state, int x, int y) { if(state == GLUT_DOWN) { selection = 0; /* mouse should only hit _one_ of the cells, so adding up all the hits just propagates a single hit. */ selection += cellHit(&curveParams[0], x, y); selection += cellHit(&pickedPoint[0], x, y); selection += cellHit(&pickedPoint[1], x, y); selection += cellHit(&pickedPoint[2], x, y); } old_y = y; redisplayAll(); } //sets values of pickedPoint to the correct point in the points array void setPoint() { points[selectedPoint][0] = pickedPoint[0].value; points[selectedPoint][1] = pickedPoint[1].value; points[selectedPoint][2] = pickedPoint[2].value; } // The "update" argument is the number of steps of the cell's value. // Anytime we create a new cell (or delete one), this function must be // modified. void cellUpdateAll(int update) { cellUpdate(&curveParams[0], update); cellUpdate(&pickedPoint[0], update); cellUpdate(&pickedPoint[1], update); cellUpdate(&pickedPoint[2], update); } //allows for click and drag void commandMotion(int x, int y) { cellUpdateAll(old_y-y); setPoint(); old_y = y; redisplayAll(); } void printVals(unsigned char key, int x, int y) { int i; int numPoints = (int)curveParams[0].value; printf("numPoints (order) = %i\n", numPoints); printf("Your current control points are: \n"); for(i=0; i