home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / inventor / noodle / Triangulator.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  18.9 KB  |  486 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. //
  18. // Code for a Generalized Cylinder class.  This is not really very
  19. // robust or well-designed (or complete or...).
  20. //
  21.  
  22. #include <stdio.h>
  23. #include <Inventor/SbLinear.h>
  24. #include <Inventor/fields/SoMFLong.h>
  25. #include <Inventor/fields/SoMFVec3f.h>
  26.  
  27. #include "Triangulator.h"
  28.  
  29.  
  30.  
  31. //**********************************************************************
  32. //                                                                      
  33. // Returns FALSE if the two line segments intersect each other at only  
  34. // one point.                                                            
  35. // Returns TRUE if they are parallel or if they intersect at a point    
  36. //    external to either line segment.                                    
  37. //                                                                      
  38. //    Assumes that the four points lie in the y = 0 plane.                
  39. //                                                                      
  40. //**********************************************************************
  41.  
  42. SbBool    
  43. Triangulator::triangEdgeTest(const SoMFVec3f &coords, 
  44.                 long e0p0, long e0p1,  long e1p0,  long e1p1 )
  45. {
  46.     float x00 = coords[(int) e0p0][0];
  47.     float x10 = coords[(int) e1p0][0];
  48.     float z00 = coords[(int) e0p0][2];
  49.     float z10 = coords[(int) e1p0][2];
  50.  
  51.     float dx0 = coords[(int) e0p1][0] - x00;
  52.     float dx1 = coords[(int) e1p1][0] - x10;
  53.     float dz0 = coords[(int) e0p1][2] - z00;
  54.     float dz1 = coords[(int) e1p1][2] - z10;
  55.  
  56.     float diffx = x10 - x00;
  57.     float diffz = z10 - z00;
  58.  
  59.     float determ = dx0 * -dz1 - dz0 * -dx1;
  60.  
  61.     if (determ == 0) // lines are parallel 
  62.         return( TRUE );
  63.     
  64.     // otherwise, solve for intersection point 
  65.     // first, solve for parametric length on edge 1 from e0p0 to e0p1 
  66.     float parametric_val = (-dz1 * diffx + dx1 * diffz) / determ;
  67.  
  68. #define SMALL_VAL        0.00001
  69.  
  70.     if (parametric_val > 1.0 - SMALL_VAL  || parametric_val < SMALL_VAL)
  71.         // value lies outside line segment 
  72.         return( TRUE );
  73.  
  74.     // next, solve for parametric length on edge 1 from e1p0 to e1p1 
  75.     parametric_val = (-dz0 * diffx + dx0 * diffz) / determ;
  76.  
  77.     if (parametric_val > 1.0 - SMALL_VAL || parametric_val < SMALL_VAL)
  78.         // value lies outside line segment 
  79.         return( TRUE );
  80.  
  81. #undef SMALL_VAL
  82.  
  83.     // otherwise, intersection point lies within both segments 
  84.     return(FALSE);
  85. }
  86.  
  87. SbBool 
  88. Triangulator::clockWiseTest( const SoMFVec3f &coords, 
  89.              const SoMFLong &indices, int startVert, int numVerts )
  90. {
  91.     // ARE THE COORDS GIVEN BY THIS POLYGON CLOCKWISE ORDERED WHEN VIEWED
  92.     // FROM ABOVE?  
  93.     // ASSUME THAT ALL THE POINTS LIE IN THE Y=0 PLANE.
  94.     // How do we know? Well, the total of the interior angles 
  95.     // for an n-sided polygon is:                                
  96.     //         interior_angles = (PI * n) - (2 * PI)                
  97.     // while:                                                    
  98.     //        exterior_angles = (PI * n) + (2 * PI)                
  99.     // So we total up the interior angles, and reverse the
  100.     // order of the vertices if it is the wrong amount
  101.     float angle_total = 0;
  102.     for( int vNum = 0; vNum < numVerts; vNum++ ) {
  103.     int nextP = (vNum == numVerts - 1) ? 0 : vNum + 1;
  104.     int prevP = (vNum == 0) ? numVerts - 1 : vNum - 1;
  105.     SbVec3f nextEdge = coords[(int) indices[startVert + nextP]] -
  106.                coords[(int) indices[startVert + vNum]];
  107.     SbVec3f prevEdge = coords[(int) indices[startVert + prevP]] -
  108.                coords[(int) indices[startVert + vNum]];
  109.     nextEdge.normalize();
  110.     prevEdge.normalize();
  111.     
  112.     // WE WANT ALL ANGLES IN THE SENSE OF ROTATING ABOUT THE 
  113.     // POSITIVE Y AXIS 
  114.     SbVec3f crossProd = prevEdge.cross( nextEdge );
  115.     if (crossProd[1] > 0.0) {
  116.         angle_total += acos( prevEdge.dot( nextEdge ) );
  117.     }
  118.     else {
  119.         angle_total += 2 * M_PI - acos( prevEdge.dot( nextEdge ) );
  120.     }
  121.     }
  122.     if (  angle_total > M_PI * numVerts )
  123.     return FALSE;
  124.     else
  125.     return TRUE;
  126. }
  127.  
  128. //**********************************************************************
  129. //                                                                      
  130. // Returns TRUE if the first point lies inside the triangle formed by   
  131. //    the other three.                                                     
  132. //                                                                      
  133. // Assumes that all of the points lie in the plane y = 0                
  134. // Assumes that the triangle is ordered clockwise when viewed from above
  135. // when travelling in the order: pt0 to pt1 to pt2                        
  136. //                                                                      
  137. // Performs the check by doing a cross product between edges of the     
  138. // triangle and vectors connecting the vertices to the test point.      
  139. // If the point is inside the triangle, the cross products are positive.
  140. // If any are negative, the point lies outside the trianggle.            
  141. // We only need to check the y values of the cross product, since the   
  142. // points lie in a horizontal plane.                                    
  143. //**********************************************************************
  144.  
  145. SbBool    
  146. Triangulator::triangInsideTest( const SoMFVec3f &coords, 
  147.                  long testPt, long pt0, long pt1, long pt2)
  148. {
  149.     SbVec3f edge0 = coords[(int)pt1] - coords[(int)pt0];
  150.     SbVec3f edge1 = coords[(int)pt2] - coords[(int)pt1];
  151.     SbVec3f edge2 = coords[(int)pt0] - coords[(int)pt2];
  152.  
  153.     SbVec3f ptEdge0 = coords[(int)testPt] - coords[(int)pt0];
  154.     SbVec3f ptEdge1 = coords[(int)testPt] - coords[(int)pt1];
  155.     SbVec3f ptEdge2 = coords[(int)testPt] - coords[(int)pt2];
  156.  
  157. #define TEENY_VAL        0.0000001
  158.  
  159.     if (     ptEdge0[2] * edge0[0] - ptEdge0[0] * edge0[2] > TEENY_VAL
  160.       &&    ptEdge1[2] * edge1[0] - ptEdge1[0] * edge1[2] > TEENY_VAL
  161.       &&    ptEdge2[2] * edge2[0] - ptEdge2[0] * edge2[2] > TEENY_VAL)
  162.         return( TRUE );
  163.     else 
  164.         return( FALSE );
  165.  
  166. #undef TEENY_VAL
  167.  
  168. }
  169.  
  170. //**********************************************************************
  171. //                                                                      
  172. // 1] assume all points lie in the y = 0 plane 
  173. // 2] perform triangulation on the projected polygon, copy the order later 
  174. // 3] for each point, find two others that form a triangle which meets 
  175. //      the following conditions:                                         
  176. //        a] Doesn't cut through any other edges                            
  177. //        b] Any new edges are on the interior                            
  178. //                                                                      
  179. //**********************************************************************
  180.  
  181. SbBool 
  182. Triangulator::triangulate( 
  183.             const SoMFVec3f &coords, 
  184.                 const SoMFLong  &inVals, // indices describing the input
  185.                              // polygons
  186.                 SoMFLong  &outVals) // indices describing the output
  187.                        // polygons
  188. {
  189.     // CHECK THAT ALL COORDS LIE IN THE y = 0.0 PLANE 
  190.     for( int i = 0; i < coords.getNum(); i++ ){
  191.     if ( coords[i][1] != 0.0 ) {
  192.         fprintf(stderr,"Error in triangulation: not all points \n");
  193.         fprintf(stderr,"  lie in the y = 0 plane\n");
  194.         return FALSE;
  195.     }
  196.     }
  197.  
  198.     // COPY THE INPUT INTO THE OUTPUT
  199.     if ( outVals.getNum() < inVals.getNum() )
  200.      outVals.insertSpace( 0, inVals.getNum() - outVals.getNum());
  201.     else if ( outVals.getNum() > inVals.getNum() )
  202.           outVals.deleteValues( 0, outVals.getNum() - inVals.getNum());
  203.     outVals = inVals;
  204.     
  205.  
  206.     int curInd0 = 0; 
  207.     int curNumVerts = 0;
  208.  
  209.     SbBool     got_a_tri, tri_ok, keep_going;
  210.     SbVec3f    edge2, edge0, prevEdge, nextEdge;
  211.     int        ind0, ind1, ind2, test_ind0, test_ind1;
  212.     int        vNum;
  213.  
  214.     //***** WALK DOWN THE LIST OF POLYS *****
  215.     
  216.     int shift_count = 0;
  217.  
  218.     for ( curInd0 = 0; curInd0 < outVals.getNum(); ) {
  219.     for( int j = curInd0, curNumVerts = 0; j < outVals.getNum(); j++ ) {
  220.         if ( outVals[j] >= 0 )
  221.         curNumVerts++;
  222.         else
  223.         break;
  224.     }
  225.  
  226.     // IF ANY THREE CONSECUTIVE POINTS ARE COLINEAR, REMOVE THE 
  227.     // CENTER POINT
  228.     // Note: we don't have to check that there are three points.
  229.     // The algorithm will just wind up removing all the points
  230.     // if there are two or less, or if all the points in the polygon
  231.     // are colinear.
  232.     for( vNum = 0; vNum < curNumVerts; vNum++ ) {
  233.         int nextP = (vNum == curNumVerts - 1) ? 0 : vNum +1;
  234.         int prevP = (vNum == 0) ? curNumVerts - 1 : vNum - 1;
  235.         nextEdge = coords[(int) outVals[curInd0 + nextP]] - 
  236.                coords[(int) outVals[curInd0 + vNum]];
  237.         prevEdge = coords[(int) outVals[curInd0 + prevP]] - 
  238.                coords[(int) outVals[curInd0 + vNum]];
  239.         
  240.         // CROSS PRODUCT IS ZERO IF THEY ARE CO-LINEAR
  241.         SbVec3f crossProd = prevEdge.cross( nextEdge );
  242.  
  243. #define TEENY_VAL        0.0000001
  244. #define IS_COORD_TEENY(a)  ( (a)[0] < TEENY_VAL && (a)[1] < TEENY_VAL \
  245.               && (a)[2] < TEENY_VAL && (a)[0] > -TEENY_VAL \
  246.               && (a)[1] > -TEENY_VAL && (a)[2] > -TEENY_VAL )
  247.  
  248.         if (IS_COORD_TEENY( crossProd) ) {
  249.         // remove the index of this point from the poly
  250.         outVals.deleteValues( curInd0 + vNum, 1);
  251.  
  252.         curNumVerts--;
  253.  
  254.         vNum--;  // decrement, because everyone's been shifted
  255.              // down by 1
  256.         }
  257. #undef TEENY_VAL
  258. #undef IS_COORD_TEENY
  259.     }
  260.  
  261.     if ( curNumVerts == 0) {
  262.         // The polygon has no vertices (this could happen because the
  263.         // whole polygon was colinear and got removed in the step above.)
  264.         // Remove the '-1' index that signals the end of the polygon.
  265.         outVals.deleteValues( curInd0, 1 );
  266.  
  267.         // Do not increment curInd0. By removing the '-1' entry, the
  268.         // next polygon moves up a slot to have *ITS* first index at
  269.         // curInd0.
  270.     }
  271.     else if ( curNumVerts <= 3) {
  272.         // Just move on to the next polygon.
  273.         // (We add an extra 1, because each polygon has a
  274.         // extra index entry of -1 to indicate the end of the poly
  275.         curInd0 += curNumVerts + 1;
  276.     }
  277.     else {
  278.  
  279.         // REVERSE ORDER IF NECESSARY
  280.         if ( !clockWiseTest( coords, outVals, curInd0, curNumVerts) ) {
  281.         long *outIndices = outVals.startEditing();
  282.  
  283.         long tempI, early, late;
  284.         for ( early = 0, late = curNumVerts - 1;
  285.               early < late; early++, late-- ) {
  286.             tempI = outIndices[curInd0 + early];
  287.             outIndices[curInd0 + early] = outIndices[curInd0 + late];
  288.             outIndices[curInd0 + late] = tempI;
  289.         }
  290.  
  291.         outVals.finishEditing();
  292.             }
  293.         
  294.             // find a triangle formed by the first point and two other points
  295.             // The new triangle must satisfy the condition that no new      
  296.             // Edges intersect any already existing edges in the polygon
  297.             ind0 = curInd0;
  298.             keep_going = TRUE;
  299.             for (ind1 = curInd0 + 1, ind2 = curInd0 + 2, got_a_tri = FALSE; 
  300.                  got_a_tri == FALSE && keep_going == TRUE;   ) {
  301.                 // TEST EACH NEW EDGE AGAINST ALL OTHER EDGES
  302.                 tri_ok = TRUE;
  303.  
  304.                 // make sure ordering of new triangle is clockwise 
  305.                 // Normal points down if (edge2 cross edge0)[1] < 0.0 
  306.                 edge2 = coords[(int)outVals[ind2]] - coords[(int)outVals[ind0]];
  307.                 edge0 = coords[(int)outVals[ind1]] - coords[(int)outVals[ind0]];
  308.                 if (edge0[0] * edge2[2] - edge2[0] * edge0[2] < 0.0)
  309.                     tri_ok = FALSE;
  310.                 
  311.                 // make sure no other points fall inside the newly formed 
  312.                 // triangle
  313.                 if (tri_ok == TRUE) {
  314.                     for( vNum = curInd0; vNum < curInd0 + curNumVerts; vNum++) {
  315.                         if (vNum != ind0 && vNum != ind1 && vNum != ind2) {
  316.                             if ( triangInsideTest(coords,outVals[vNum],outVals[ind0], outVals[ind1], outVals[ind2]))
  317.                                 tri_ok = FALSE;
  318.                         }
  319.                     }
  320.                 }
  321.  
  322.                 // edge between ind0 and ind1
  323.                 if (ind1 != curInd0 + 1) {
  324.                     // edge between point0 and point1 is ok, since part of 
  325.                     // original polygon 
  326.                     for( test_ind0 = curInd0, test_ind1 = curInd0 + 1;
  327.                          tri_ok == TRUE && test_ind0 < curInd0 + curNumVerts;
  328.                          test_ind0++, test_ind1++) { 
  329.                         if (test_ind1 == curInd0 + curNumVerts)
  330.                             test_ind1 = curInd0;
  331.                         tri_ok = triangEdgeTest(coords, outVals[ind0], outVals[ind1], 
  332.                                     outVals[test_ind0], outVals[test_ind1]);
  333.                     }
  334.                 }
  335.  
  336.                 // edge between ind1 and ind2 is part of the original polygon
  337.                 // except when forming tri between v0, v1, and final point 
  338.                 if (ind2 != ind1 + 1) {
  339.                     for( test_ind0 = curInd0, test_ind1 = curInd0 + 1;
  340.                          tri_ok == TRUE && test_ind0 < curInd0 + curNumVerts;
  341.                          test_ind0++, test_ind1++) { 
  342.                         if (test_ind1 == curInd0 + curNumVerts)
  343.                             test_ind1 = curInd0;
  344.                         tri_ok = triangEdgeTest(coords, outVals[ind1], outVals[ind2], 
  345.                                     outVals[test_ind0], outVals[test_ind1]);
  346.                     }
  347.                 }
  348.  
  349.                 // edge between ind2 and ind0 
  350.                 if (ind2 != curInd0 + curNumVerts -1) {
  351.                     // edge between point0 and last point in poly is ok,since
  352.                     // part of original polygon 
  353.                     for( test_ind0 = curInd0, test_ind1 = curInd0 + 1;
  354.                          tri_ok == TRUE && test_ind0 < curInd0 + curNumVerts;
  355.                          test_ind0++, test_ind1++) { 
  356.                         if (test_ind1 == curInd0 + curNumVerts)
  357.                             test_ind1 = curInd0;
  358.                         tri_ok = triangEdgeTest(coords, outVals[ind2], outVals[ind0], 
  359.                                     outVals[test_ind0], outVals[test_ind1]);
  360.                     }
  361.                 }
  362.                 if (tri_ok == TRUE)
  363.                     got_a_tri = TRUE;
  364.                 else {
  365.                     if (ind1 == curInd0 + 1 && ind2 == curInd0 + curNumVerts -1)
  366.                         keep_going = FALSE;
  367.                     ind1++;
  368.                     ind2++;
  369.                     if (ind2 == curInd0 + curNumVerts) {
  370.                         // If none others worked, try the case where the 
  371.                         // the points are first, second, and last          
  372.                         ind1 = curInd0 + 1;
  373.                         ind2 = curInd0 + curNumVerts - 1;
  374.                     }
  375.                 }
  376.             }
  377.  
  378.             if (got_a_tri == FALSE) {
  379.                 // Could not draw a triangle using the first vertex. 
  380.                 // Shift all of the vertices in the polygon over and try again 
  381.                 shift_count++;
  382.  
  383.         long *outIndices = outVals.startEditing();
  384.  
  385.                 long temp_index = outIndices[ curInd0 + 0];
  386.  
  387.                 for( vNum = curInd0; vNum < curInd0 + curNumVerts - 1; vNum++ )
  388.                     outIndices[ vNum] = outIndices[ vNum + 1];
  389.                 outIndices[ curInd0 + curNumVerts - 1] = temp_index;
  390.  
  391.         outVals.finishEditing();
  392.  
  393.                 if (shift_count >= curNumVerts) {
  394.                     printf("Error: could not triangulate this polygon\n");
  395.                     printf("It may look funny!\n");
  396.             return(FALSE );
  397.                 }
  398.  
  399.         // Do not increment the value of curInd0.
  400.         // This will mean that the 'for' loop will repeat the entire
  401.         // process on this polygon (but THIS time, the indices have
  402.         // been shifted in order.
  403.             }
  404.             else {
  405.             
  406.                 shift_count = 0;
  407.  
  408.                 // Create a new triangle which connects the first point 
  409.                 // to these two new points 
  410.         // Add this new triangle to the end of the outVals
  411.  
  412.         long newIndices[4];
  413.         newIndices[0] = outVals[ ind0];
  414.         newIndices[1] = outVals[ ind1]; 
  415.         newIndices[2] = outVals[ ind2]; 
  416.         newIndices[3] = -1;
  417.  
  418.         outVals.setValues( outVals.getNum(), 4, newIndices );
  419.  
  420.                 // Make necessary changes to the old polygon 
  421.                 if (ind1 == curInd0 + 1 && ind2 == curInd0 + curNumVerts - 1) {
  422.                     // triangle was first, second, and last points 
  423.                     // we need to get rid of first point in the current poly
  424.             outVals.deleteValues( curInd0, 1);
  425.                     curNumVerts--;
  426.                 }
  427.                 else if (ind1 == curInd0 + 1) {
  428.                     // triangle was first three points 
  429.                     // we need to get rid of second point 
  430.             outVals.deleteValues( curInd0 + 1, 1 );
  431.                     curNumVerts --;
  432.                 }
  433.                 else if (ind2 == curNumVerts - 1) {
  434.                     // triangle was last three points 
  435.             // we need to get rid of last point
  436.             outVals.deleteValues( curInd0 + curNumVerts - 1, 1 );
  437.                     curNumVerts --;
  438.                 }
  439.                 else {    
  440.                     // we need to break the poly into 2, since we just created
  441.                     // an interior-type triangle 
  442.                     // The first polygon will go from ind0 to ind1 
  443.                     // and will reside in the original polygon data structure 
  444.                     // The second will continue from ind2 back to ind0 
  445.                     // and will be added to the end of outVals.
  446.  
  447.             // FIRST, ADD THE LATTER POLY TO THE END OF outVals...
  448.  
  449.             int newPolNumVerts = curInd0 + curNumVerts - ind2 + 1;
  450.  
  451.             // Add an extra slot for the '-1' which signifies end
  452.             // of polygon
  453.             int newPolInd0 = outVals.getNum();
  454.             outVals.insertSpace( newPolInd0, newPolNumVerts + 1 ); 
  455.  
  456.             long *outIndices = outVals.startEditing();
  457.             // Copy the points from ind2 to the end of the poly
  458.             for ( int j = newPolInd0, vNum = ind2;
  459.               vNum < curInd0 + curNumVerts; j++, vNum++ ) {
  460.               outIndices[j] = outIndices[vNum];
  461.             }
  462.             // Copy the initial point...
  463.             outIndices[newPolInd0 + newPolNumVerts - 1] 
  464.             = outIndices[curInd0];
  465.  
  466.             // Add the -1 to signify the end
  467.             outIndices[newPolInd0 + newPolNumVerts] = -1;
  468.  
  469.             outVals.finishEditing();
  470.  
  471.  
  472.             // NEXT, REMOVE THE LATTER HALF OF  THE FIRST POLY...
  473.             outVals.deleteValues( ind2, newPolNumVerts - 1 );
  474.  
  475.                     // Reduce the num of coords being used from the original
  476.                     // polygon 
  477.                     curNumVerts -= newPolNumVerts - 1;
  478.                 }
  479.             }
  480.         }
  481.     }
  482.     
  483.     return TRUE;
  484. }
  485.  
  486.