/**************************** main.c *****************************/ /* */ /* This is part of the "ming" program. This part contains the */ /* procedures related to knot drawing using OpenInventor. */ /* */ /********************** Ying-Qing Wu, 12/95 **********************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "class.h" SoXtExaminerViewer *myViewer; SoSeparator *root = new SoSeparator; SoMaterial *editedMaterial = new SoMaterial; SoMaterial *BGMaterial = new SoMaterial; SoSelection *selectionRoot; Widget myWindow; double EdgeRadius = 2; int Sides = 8; int index = -1; int selectedFlag = 0; extern int renderStatus; extern Widget textWidgets[]; extern int ctr, colorStatus, colorPeriod, vnum; extern double enr; extern knots knot, vtr; extern FILE *fp; extern int knotData(); void showData() { char out[20]; sprintf(out, "%.8f", enr); XmTextSetString (textWidgets[1], out); sprintf(out, "%d", ctr); XmTextSetString (textWidgets[2], out); sprintf(out, "%d", vnum); XmTextSetString (textWidgets[3], out); } void erase() { int n = root->getNumChildren(); for (int i=n-1; i>=0; i--) { SoSeparator *sep = (SoSeparator*)root->getChild(i); sep->removeAllChildren(); } root->removeAllChildren(); } void eraseTriangles() { int n = root->getNumChildren(); for (int i=n-1; i>=2*vnum; i--) { SoSeparator *sep = (SoSeparator*)root->getChild(i); sep->removeAllChildren(); root->removeChild(i); } } double colorF (double x) { if (x < 0) x = -x; while (x>0.5) x--; if (x < 0) x = -x; return (x<1.0/6) ? 1 : (x<2.0/6) ? 2-6*x : 0; } float *getColor(int k, int vnum) { static float rgb[3]; // RGB value of color if (colorStatus == 0) { rgb[0] = colorF(k*colorPeriod*1.0/vnum); rgb[1] = colorF(k*colorPeriod*1.0/vnum + 2.0/3); rgb[2] = colorF(k*colorPeriod*1.0/vnum + 1.0/3); } else if (colorStatus == 1) { double zMax=knot[0][2], zMin=knot[0][2], rate; for (int i=0; i knot[i][2]) zMin = knot[i][2]; } rate = (knot[k][2]-zMin)/(zMax-zMin); rgb[0] = colorF( rate * 2.0/3); rgb[1] = colorF((rate+1) * 2.0/3); rgb[2] = colorF((rate+2) * 2.0/3); } else // not used rgb[0] = 0, rgb[1] = 1, rgb[2] = 0; return rgb; } SoXtExaminerViewer *buildMainWindow(Widget, SoNode *); float vertexPositions[2*GON+2][3]; float normalVectors[2*GON+2][3]; SoSeparator *makeStrip(int k, int vnum) { float colors[2*GON+2][3]; for (int j=0; j<2; j++) { float *ptr = getColor((k+j>vnum)?0:(k+j)%vnum, vnum); for (int i=0; i<=Sides; i++) for (int n=0; n<3; n++) colors[2*i+j][n] = *(ptr+n); } SoSeparator *result = new SoSeparator; result->ref(); SoShapeHints *myHints = new SoShapeHints; myHints->vertexOrdering = SoShapeHints::CLOCKWISE; result->addChild(myHints); // Define colors for the strip if (colorStatus > 1) result->addChild(editedMaterial); else { SoMaterial *myMaterials = new SoMaterial; myMaterials->shininess.setValue(1); myMaterials->specularColor.setValue(1,1,1); myMaterials->diffuseColor.setValues(0, 2*Sides+2, colors); result->addChild(myMaterials); } SoMaterialBinding *myMaterialBinding = new SoMaterialBinding; myMaterialBinding->value = SoMaterialBinding::PER_VERTEX; result->addChild(myMaterialBinding); // Define normals for the strip SoNormal *myNormals = new SoNormal; myNormals->vector.setValues(0, 2*Sides+2, normalVectors); result->addChild(myNormals); SoNormalBinding *myNormalBinding = new SoNormalBinding; myNormalBinding->value = SoNormalBinding::PER_VERTEX; result->addChild(myNormalBinding); // Define coordinates for vertices SoCoordinate3 *myCoords = new SoCoordinate3; myCoords->point.setValues(0, 2*Sides+2, vertexPositions); result->addChild(myCoords); // Define the TriangleStripSet SoTriangleStripSet *myStrips = new SoTriangleStripSet; myStrips->numVertices.setValue(2*Sides+2); result->addChild(myStrips); result->unrefNoDelete(); return result; } void drawKnot0(knots knot, int vnum) { for (int k=0; kref(); float colors[2][3]; for (int j=0; j<2; j++) { float *ptr = getColor((k+j)%vnum, vnum); for (int n=0; n<3; n++) colors[j][n] = *(ptr+n); } if (colorStatus > 1) result->addChild(editedMaterial); else { SoMaterial *myMaterials = new SoMaterial; myMaterials->shininess.setValue(1.0); myMaterials->specularColor.setValue(1,1,1); myMaterials->diffuseColor.setValues(0, 2, colors); result->addChild(myMaterials); SoMaterialBinding *myMaterialBinding = new SoMaterialBinding; myMaterialBinding->value = SoMaterialBinding::PER_VERTEX; result->addChild(myMaterialBinding); } // Define coordinates for vertices SoCoordinate3 *myCoords = new SoCoordinate3; float coord[2][3]; for (int i=0; i<2; i++) for (j=0; j<3; j++) coord[i][j] = knot[(k+i)%vnum][j]; myCoords->point.setValues(0, 2, coord); result->addChild(myCoords); SoDrawStyle *myStyle = new SoDrawStyle; myStyle->style = SoDrawStyle::LINES; myStyle->lineWidth = 3*EdgeRadius; result->addChild(myStyle); // Define the Line SoLineSet *myLine = new SoLineSet; myLine->numVertices.setValue(2); result->addChild(myLine); result->unrefNoDelete(); root->addChild (result); } } void drawKnot1(knots knot, int vnum) { knots dir, vtr; vector K0, e0, e1, e2, nv; vector iniPts[GON], iniNormal[GON], pts[2][GON], normal[2][GON]; int i, j, n; for (i=0; iaddChild (sep); for (j=0; j 1) sep->addChild(editedMaterial), sep2->addChild(editedMaterial); else { mat->shininess.setValue(1.0); mat->diffuseColor.setValue(getColor(k, vnum)); mat->specularColor.setValue( 1,1,1); sep->addChild (mat); sep2->addChild (mat); } sph->radius.setValue( 1.01* EdgeRadius ); xform->translation.setValue(knot[k].coord[0], knot[k].coord[1], knot[k].coord[2]); sep2->addChild (xform); sep2->addChild (sph); root->addChild (sep2); double x = edge[0], y = edge[1], z = edge[2]; angle = ( x*x + z*z == 0 ) ? 0 : ( y == 0 ) ? Pi/2 : ( y > 0 ) ? atan( sqrt ( x*x + z*z ) / y ) : Pi + atan( sqrt( x*x + z*z ) / y ); xform2->rotation.setValue( SbVec3f( z , 0 , -x ) , angle ); temp = knot[k] + (0.5 * edge); xform2->translation.setValue (temp.coord[0],temp.coord[1],temp.coord[2]); cyl->height = edge.norm(); cyl->radius = EdgeRadius; sep->addChild (xform2); sep->addChild (cyl); root->addChild (sep); } } SoSeparator * drawTriangle(int i) { int j, k; float vertices[3][3]; for(j=0; j<3; j++) for (k=0; k<3; k++) vertices[j][k] = knot[(i-1+j+vnum)%vnum][k]; SoSeparator *triang = new SoSeparator(); triang->ref(); // A shape hints tells the ordering of polygons. // This insures double sided lighting. SoShapeHints *myHints = new SoShapeHints; myHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; triang->addChild(myHints); // Define material SoMaterial *myMaterial = new SoMaterial; myMaterial->diffuseColor.setValue(getColor(i, vnum)); myMaterial->shininess.setValue(0); triang->addChild(myMaterial); // Define coordinates for vertices SoCoordinate3 *myCoords = new SoCoordinate3; myCoords->point.setValues(0, 3, vertices); triang->addChild(myCoords); // Define the FaceSet SoFaceSet *myFaceSet = new SoFaceSet; myFaceSet->numVertices.setValue(3); triang->addChild(myFaceSet); triang->unrefNoDelete(); return triang; } void render3(int Flag) { erase(); for (int k=0; kshininess.setValue(1.0); mat->diffuseColor.setValue(1, 0.4, 0); mat->specularColor.setValue( 1,1,1); sep->addChild (mat); SoMaterial *mat2 = new SoMaterial; mat2->shininess.setValue(1.0); mat2->diffuseColor.setValue(0, 0.5, 1); mat2->specularColor.setValue( 1,1,1); sep2->addChild (mat2); sph->radius.setValue (EdgeRadius); xform->translation.setValue(knot[k].coord[0], knot[k].coord[1], knot[k].coord[2]); sep2->addChild (xform); sep2->addChild (sph); root->addChild (sep2); double x = edge[0], y = edge[1], z = edge[2]; angle = ( x*x + z*z == 0 ) ? 0 : ( y == 0 ) ? Pi/2 : ( y > 0 ) ? atan( sqrt ( x*x + z*z ) / y ) : Pi + atan( sqrt( x*x + z*z ) / y ); xform2->rotation.setValue( SbVec3f( z , 0 , -x ) , angle ); temp = knot[k] + (0.5 * edge); xform2->translation.setValue (temp.coord[0],temp.coord[1],temp.coord[2]); cyl->height = edge.norm(); cyl->radius = EdgeRadius/1.5; sep->addChild (xform2); if (k!=vnum-1 || Flag) sep->addChild (cyl); root->addChild (sep); } showData(); myViewer->render(); } void render2(int i) { root->addChild(drawTriangle(i)); myViewer->render(); } void render() { extern int drawFlag; extern int smoothFlag; // draw smooth/polygonal knots extern int smoothVnum; extern void normalize(); myViewer->setBackgroundColor(*((BGMaterial->diffuseColor).getValues(0))); erase(); if (drawFlag) { normalize(); render3(1); return; } if (vnum>2) { normalize(); if (smoothFlag) { knots showKnot; vector e1, e2; int k = smoothVnum/vnum + 1; int showVnum = k * vnum; int i, j; for (i=0; irender(); } int videoGet (FILE *fp) { int i, j = -2, n; knots myKnot; char str[80]; int myVnum; extern SoXtExaminerViewer *myViewer; while (1) { if (!fgets(str, 80, fp)) return 0; for (i=0, n=0; i510) return 0; } else if (sscanf(str,"%lf %lf %lf", &myKnot[j][0],&myKnot[j][1],&myKnot[j][2])<3) continue; if (j == myVnum -1) break; } vnum = myVnum; knot = myKnot; return 1; } void error(Widget parent, XtPointer client_data, XtPointer) { char *errorMsg = (char *) client_data;; Widget dialog; XmString text = XmStringCreateLtoR (errorMsg, XmFONTLIST_DEFAULT_TAG); extern void Cancel_CB(Widget, XtPointer, XtPointer); Arg args[5]; int n; n = 0; XtSetArg (args[n], XmNmessageString, text); n++; dialog = XmCreateErrorDialog (parent, "error", args, n); XtUnmanageChild (XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON)); XtUnmanageChild (XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON)); XtAddCallback (dialog, XmNokCallback, Cancel_CB, NULL); XmStringFree (text); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); } void videoPlay(char *vfile, float) { FILE *fp; if (!(fp = fopen(vfile, "r"))) { error(myWindow, "ERROR: Can not open Video file.", NULL); printf("Can not open Video file %s.\n", vfile); return; } ctr = 0; enr = 0; while (videoGet(fp)) { ctr++; render(); } fclose(fp); } SoSeparator *text() { SoSeparator *scene = new SoSeparator; SoSeparator *sep1 = new SoSeparator; SoSeparator *sep2 = new SoSeparator; scene->addChild(sep1); // Choose a font SoFont *myFont = new SoFont; myFont->name.setValue("Times-Roman"); myFont->size.setValue(2.0); sep1->addChild(myFont); SoFont *myFont1 = new SoFont; myFont1->name.setValue("Times-Roman"); myFont1->size.setValue(0.1); sep2->addChild(myFont1); BGMaterial->diffuseColor.setValue(0,0.6,0.9); editedMaterial->specularColor.setValue(0,0,0); editedMaterial->diffuseColor.setValue(0,1,0); sep1->addChild(editedMaterial); sep2->addChild(editedMaterial); SoTransform *xfm = new SoTransform; SoText3 *myText = new SoText3; myText->parts = SoText3::ALL; myText->string = "MING"; sep1->addChild(xfm); sep1->addChild(myText); return scene; } void selectionCallback(void *, SoPath *selectionPath) { extern int drawFlag; if (!drawFlag) return; SoPath *xformPath = selectionPath; if (xformPath == NULL) return; SoSeparator * sep = (SoSeparator*) xformPath->getNodeFromTail(1); int k = root->findChild (sep); if (k >= 2*vnum) { selectionPath = NULL; SoSeparator *sep = (SoSeparator*)root->getChild(k); sep->removeAllChildren(); root->removeChild(k); return; } index = k; ((SoMaterial*) sep->getChild(0)) ->diffuseColor.setValue(1, 0, 0); selectedFlag=1; } void deselectionCallback(void *, SoPath *) { extern int drawFlag; if (!drawFlag) return; SoSeparator *sep = (SoSeparator*)(root->getChild(index)); SoMaterial * mat = (SoMaterial*) (sep->getChild(0)); if (index%2) mat->diffuseColor.setValue(1, 0.4, 0); else mat->diffuseColor.setValue(0, 0.5, 1); index = -1, selectedFlag = 0; } void processKeyEvents(void *, SoEventCallback *cb ) { if (SO_KEY_PRESS_EVENT(cb->getEvent(), UP_ARROW)) { cb->setHandled(); } } void main(int argc, char **argv) { int getfile(char *); myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); selectionRoot = new SoSelection; selectionRoot->ref(); selectionRoot-> addSelectionCallback(selectionCallback, NULL); selectionRoot-> addDeselectionCallback(deselectionCallback, NULL); SoEventCallback *eventCB = new SoEventCallback; selectionRoot->addChild(eventCB); eventCB->addEventCallback(SoKeyboardEvent::getClassTypeId(), processKeyEvents, NULL); // create the scene graph selectionRoot->addChild(root); // Set up viewer: myViewer = buildMainWindow(myWindow, selectionRoot); myViewer->setTitle (argv[1]); myViewer->setBackgroundColor(*((BGMaterial->diffuseColor).getValues(0))); SoXt::show(myWindow); if (argc>1 && !getfile(argv[1])) error(myWindow, "ERROR: Can not read knot file.", NULL); else if (argc==1) { root->addChild(text()); myViewer->setBackgroundColor(*((BGMaterial->diffuseColor).getValues(0))); myViewer->render(); // render(); } myViewer->viewAll(); SoXt::mainLoop(); } /***************************************************************/ /* The following functions handle knot editing */ /***************************************************************/ void updateVertex (int k) { SoTransform *xform = (SoTransform *) ((SoSeparator *)root->getChild(2*k))->getChild(1); xform->translation.setValue(knot[k][0], knot[k][1], knot[k][2]); } void updateEdge (int k) { vector edge = knot[(k+1)%vnum] - knot[k]; double x = edge[0], y = edge[1], z = edge[2]; double angle = ( x*x + z*z == 0 ) ? 0 : ( y == 0 ) ? Pi/2 : ( y > 0 ) ? atan( sqrt ( x*x + z*z ) / y ) : Pi + atan( sqrt( x*x + z*z ) / y ); int i = 2*k + 1; SoTransform * xform = (SoTransform *) ((SoSeparator *)root->getChild(i))->getChild(1); xform->rotation.setValue( SbVec3f( z , 0 , -x ) , angle ); vector temp = knot[k] + (0.5 * edge); xform->translation.setValue (temp.coord[0],temp.coord[1],temp.coord[2]); SoCylinder *cyl = (SoCylinder*) ((SoSeparator *)root->getChild(i))->getChild(2); cyl->height = edge.norm(); } void addSelectedVertex(int ind) { for (int i=vnum; i>ind+1; i--) knot[i] = knot[i-1]; knot[ind+1] = 0.5 * (knot[ind] + knot[ind+1]); vnum++; knotData(); render(); } void removeSelectedVertex(int ind) { for (int i=ind; igetSize(); size[0] -= 61, size[1] -= 33; double minSize = (size[0] < size[1]) ? size[0] : size[1]; x = double(myEvent->x - 38) / minSize; y = double(size[1] - myEvent->y) / minSize; } SbBool myAppEventHandler(void *userData, XAnyEvent *anyevent) { SoXtExaminerViewer *myViewer = (SoXtExaminerViewer *) userData; XButtonEvent *myEvent; XMotionEvent *myMotionEvent; extern int drawFlag; if (!drawFlag) return FALSE; static vector tempV; static double x0, y0; if (anyevent->type == ButtonRelease) { myEvent = (XButtonEvent *) anyevent; if (myEvent->button == Button2 && (myEvent->state & Button1Mask)) { buttonPoint(anyevent, x0, y0); int k = (drawFlag == 1) ? vnum-1 : index/2; tempV = knot[k]; } } // draw new knot if (drawFlag == 1 && anyevent->type == ButtonPress) { myEvent = (XButtonEvent *) anyevent; buttonPoint(anyevent, x0, y0); // add a vertex if (myEvent->button == Button1) { SoCamera *myCamera = (SoCamera *) myViewer->getCamera(); SbViewVolume myViewVolume; myViewVolume = myCamera->getViewVolume(); if (vnum == 0) { SbVec3f p0, p1; myViewVolume.projectPointToLine(SbVec2f(x0,y0), p0, p1); SbVec3f intersection = (p0 + p1) / 2.0; for (int i=0; i<3; i++) knot[vnum][i] = intersection[i]; } else { SbVec3f eye = myViewVolume.getProjectionPoint(); SbVec3f zVec = myViewVolume.zVector(); vector eyeV, zVecV; for (int i=0; i<3; i++) eyeV[i] = eye[i], zVecV[i] = zVec[i]; double dist = - (1/zVecV.norm()) * (knot[0] - eyeV) * zVecV; SbVec3f pt = myViewVolume.getPlanePoint(dist, SbVec2f(x0,y0)); for (int j=0; j<3; j++) knot[vnum][j] = pt[j]; } if ( vnum && (knot[vnum]-knot[0]).norm() < 9) { drawFlag = 2; knotData(); render3(1); return TRUE; } index = -1, vnum ++; } // remove a vertex else if (myEvent->button == Button2 && !(myEvent->state & Button1Mask)) if (vnum) vnum--; index = 2*(vnum-1); tempV = knot[vnum-1]; render3(0); return TRUE; } // add or delete vertices if (drawFlag == 2 && selectedFlag && anyevent->type == ButtonPress) { buttonPoint(anyevent, x0, y0); myEvent = (XButtonEvent *) anyevent; tempV = knot[index/2]; // add or remove selected vertex if (myEvent->button == Button2 && !(myEvent->state & Button1Mask)) { if (index%2) // edge selected addSelectedVertex (index/2); else removeSelectedVertex (index/2); selectedFlag = 0; return TRUE; } else if (myEvent->button == Button3 && (myEvent->state & Button1Mask)) root->addChild(drawTriangle(index/2)); } // move knots else if ( (drawFlag == 1 && vnum || drawFlag==2 && selectedFlag) && anyevent->type == MotionNotify && index%2 == 0 ) { myMotionEvent = (XMotionEvent *) anyevent; if (!(myMotionEvent->state & (Button1Mask | Button2Mask))) return FALSE; double x, y; buttonPoint(anyevent, x, y); SoCamera *myCamera = (SoCamera *) myViewer->getCamera(); SbViewVolume myViewVolume; myViewVolume = myCamera->getViewVolume(); int k = index/2; // move a vertex horizontally if ((myMotionEvent->state & Button1Mask) && !(myMotionEvent->state & Button2Mask)) { SbVec3f eye = myViewVolume.getProjectionPoint(); SbVec3f zVec = myViewVolume.zVector(); vector eyeV, zVecV; for (int i=0; i<3; i++) eyeV[i] = eye[i], zVecV[i] = zVec[i]; double dist = - (1/zVecV.norm()) * (tempV - eyeV) * zVecV; SbVec3f pt = myViewVolume.getPlanePoint(dist, SbVec2f(x,y)); for (int j=0; j<3; j++) knot[k][j] = pt[j]; } // move vertically else if ((myMotionEvent->state & Button1Mask) && (myMotionEvent->state & Button2Mask)) { SbVec3f zVec = myViewVolume.zVector(); for (int j=0; j<3; j++) knot[k][j] = tempV[j] - 200 * (y - y0) * zVec[j]; } // update updateVertex (k); if (vnum > 1) updateEdge ((k-1+vnum)%vnum); if (drawFlag == 2) { updateEdge (k); knotData(); showData(); } return TRUE; } return FALSE; } void toggleTriangle() { int n = root->getNumChildren(); if (n > 2*vnum) eraseTriangles(); else for (int i=0; iaddChild(drawTriangle(i)); }