/* Tutor on NURB curves (1-dimension) Written by Caroline Geiersbach and Scott D. Anderson scott.anderson@acm.org Summer 2003 */ #include #include #include #include #include "tw.h" //window identifiers GLuint mainWindow, curveWindow, controlWindow, knotWindow; #define GAP 25 //gap between subwindows int cWinWidth = 450; //command window width int cWinHeight = 450; //command window height int wWinWidth = 450; //world window width int wWinHeight = 550; //world window height int mWinWidth = 900+GAP*3; //main window width int mWinHeight = 600; //main window height void redisplayAll(void) { glutSetWindow(curveWindow); glutPostRedisplay(); glutSetWindow(controlWindow); glutPostRedisplay(); glutSetWindow(knotWindow); glutPostRedisplay(); } const int NO_KNOT = -1; int knotIndex; // the index of the picked knot // ================================================================ // The model //for picking // int count; // int p=15; //unselected point // int k=31; //unselected knot //global parameters for the knots GLfloat uKnot[30]; int numControlPoints; int uOrder; int numKnots; GLUnurbsObj *myNurb; GLfloat originalPoints[15][4] = { {-7,-3,1,1}, {-6,-3,1,1}, {-5,0,1,1}, {-4,-5,1,1}, {-3,5,1,1}, {-2,-3,1,1}, {-1,3,1,1}, {0,-16,1,1}, {1,0,1,1}, {2,-3,1,1}, {3,-10,1,1}, {4,13,1,1}, {5,-10,1,1}, {6,-3,1,1},{7,-4,1,1}, }; GLfloat controlPoints[15][4]; GLfloat weights[15]; // This makes a conventional knot vector, with all knots at unit steps // except for the beginning and the end, where multiple knots force // the NURBS to interpolate the first and last control points. Adapted // from Hill pg. 635. Rescaled to 0..1 void makeKnots(int order, int numControlPoints, GLfloat knot[]) { //pts control points and B-splines of order ord int i; if(numControlPoints < order) { printf("too few points for curve of order %i\n", order); return; } GLfloat max=numControlPoints-order+1; for (i=0; i= cell->x && x <= cell->x+55 && y >= cell->y-15 && y <= cell->y+15) { return cell->id; } return 0; } int selectedCell = 0; // Given a mouse click, (mx,my), determine the index of the cell that // was hit, if any. Store result in selectedCell. This function // relies on cellHit returning zero for all cells except the one that // is hit, if any. void selectCell(int mx, int my) { selectedCell=0; selectedCell += cellHit(&curveParams[0], mx, my); selectedCell += cellHit(&curveParams[1], mx, my); selectedCell += cellHit(&pickedPoint[0], mx, my); selectedCell += cellHit(&pickedPoint[1], mx, my); selectedCell += cellHit(&pickedPoint[2], mx, my); selectedCell += cellHit(&pickedPoint[3], mx, my); } //moves information from controlWindow to corresponding global variables void setVals () { numControlPoints = (int)curveParams[0].value; uOrder = (int)curveParams[1].value; numKnots = numControlPoints + uOrder; } // updates the cell's value void cellUpdate(cell* cell, int update) { // generic code if (selectedCell != 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; if (cell->value > cell->max) cell->value = cell->max; // special case code. Cells 5 and 6 correspond to the number of // control points and curve order, so if they are changed, we have // to recalculate the knots as well. setVals(); if(cell->id ==5 || cell->id==6) { makeKnots(uOrder,numControlPoints,uKnot); } } int old_y; //for controlWindow mouse void cellDraw(cell* cell) { twColorName(TW_YELLOW); //color of numbers not currently selected if (selectedCell == cell->id) { // When a number is modified, info text is displayed at the top // of the window (10,20), saying what the number means. twColorName(TW_CYAN); twDrawString(10, 20, cell->info); } twDrawString(cell->x, cell->y, cell->format, cell->value); } const int NO_POINT = -1; // an invalid index for a control point int pickedPointIndex = NO_POINT; // a click picks and motion adjusts //display on right side; where user can adjust values void controlWindowDisplay() { curveParams[1].value = uOrder; curveParams[0].value = numControlPoints; //set up camera glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, cWinWidth, cWinHeight, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.7, 0.7, 0.7, 1.0); //clear to gray glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); twSetFont("helvetica", 18); twDrawString(10, curveParams[0].y, "number of control points:"); twDrawString(10, curveParams[1].y, "order of curve:"); cellDraw(&curveParams[0]); cellDraw(&curveParams[1]); if(pickedPointIndex != NO_POINT) { //point has been selected int p = pickedPointIndex; pickedPoint[0].value = controlPoints[p][0]; pickedPoint[1].value = controlPoints[p][1]; pickedPoint[2].value = controlPoints[p][2]; pickedPoint[3].value = weights[p]; 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, ")"); twDrawString(10,pickedPoint[3].y, "weight = "); cellDraw(&pickedPoint[0]); cellDraw(&pickedPoint[1]); cellDraw(&pickedPoint[2]); cellDraw(&pickedPoint[3]); } else { twColorName(TW_ORANGE); twDrawString(10, pickedPoint[0].y, "click on point to view its coords."); } twColorName(TW_RED); if(knotIndex != NO_KNOT) { //knot has been selected twDrawString(10, 340, "knot index: %i", knotIndex); twDrawString(10, 380, "knot value: %.2f", uKnot[knotIndex]); } else { twDrawString(10,340, "no knot selected"); } if (!selectedCell) { twColorName(TW_CYAN); twDrawString(10, 20, "Click on a cell and move mouse to modify values."); } //directions at bottom of controlWindow twColorName(TW_CYAN); twDrawString(10,425, "Hit

to print point and knot values."); twColorName(TW_WHITE); glutSwapBuffers(); } void controlWindowMouse(int button, int state, int x, int y) { if(state == GLUT_DOWN) { selectCell(x,y); } old_y = y; redisplayAll(); } //sets values of pickedPoint to the correct point in the points array void setPoint() { controlPoints[pickedPointIndex][0] = pickedPoint[0].value; controlPoints[pickedPointIndex][1] = pickedPoint[1].value; controlPoints[pickedPointIndex][2] = pickedPoint[2].value; weights[pickedPointIndex] = pickedPoint[3].value; } //allows for click and drag void controlWindowMotion(int x, int y) { // any movement less than 10 pixels doesn't count. if(abs(old_y-y)<10) return; cellUpdate(&curveParams[0], old_y-y); cellUpdate(&curveParams[1], old_y-y); cellUpdate(&pickedPoint[0], old_y-y); cellUpdate(&pickedPoint[1], old_y-y); cellUpdate(&pickedPoint[2], old_y-y); cellUpdate(&pickedPoint[3], old_y-y); setPoint(); old_y = y; redisplayAll(); } // ================================================================ // curveWindow void curveDisplay(void) { int i,j; twDisplayInit(); twCamera(); twColorName(TW_RED); glLineWidth(3); gluBeginCurve(myNurb); { GLfloat nurbsCP[numControlPoints][4]; for(i=0; i0 && uKnot[i]<1) twDrawString((int)uKnot[i], 20, "%i", i); printf("knot %d = %f\n",i,uKnot[i]); } glPopMatrix(); glutSwapBuffers(); } // Determines the array index for the chosen knot; there's exactly one. int knotPick(int x, int y) { int i; twTriple screen = {x,y,0}, world; twUnProject(world,screen); GLfloat mouse_x = world[0]; // find which knot value is closest. What about ties? Find the // pair of indices where the screen value is between the knots, and // use the closer knot. while(uKnot[i] uKnot[i+1]-mouse_x) knotIndex = i+1; else knotIndex = i; } //changes knotXVal[element] to x, finds the new knot value void knotAdjust(int x, int y) { // this calculation is essentially what unprojection would do. uKnot[knotIndex] = (float)x*(numControlPoints-uOrder+1)/(cWinWidth); } int knotMouse_x, knotMouse_y; void knotMouse(int button, int state, int x, int y) { if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN){ knotMouse_x=x; knotMouse_y=y; knotPick(x,y); } redisplayAll(); } void knotMotion(int x, int y) { knotAdjust(x,y); redisplayAll(); } void printVals(unsigned char key, int x, int y) { int i; int numControlPoints = (int)curveParams[0].value; int order = (int)curveParams[1].value; printf("numControlPoints = %i, order = %i\n", numControlPoints, order); printf("Your current control points and their weights are: \n"); for(i=0; i