polygon_cube_intersection.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002 *       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 3      *
00003 *                (c) 2006-2008 MGH, INRIA, USTL, UJF, CNRS                    *
00004 *                                                                             *
00005 * This library is free software; you can redistribute it and/or modify it     *
00006 * under the terms of the GNU Lesser General Public License as published by    *
00007 * the Free Software Foundation; either version 2.1 of the License, or (at     *
00008 * your option) any later version.                                             *
00009 *                                                                             *
00010 * This library is distributed in the hope that it will be useful, but WITHOUT *
00011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
00012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
00013 * for more details.                                                           *
00014 *                                                                             *
00015 * You should have received a copy of the GNU Lesser General Public License    *
00016 * along with this library; if not, write to the Free Software Foundation,     *
00017 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
00018 *******************************************************************************
00019 *                              SOFA :: Framework                              *
00020 *                                                                             *
00021 * Authors: M. Adam, J. Allard, B. Andre, P-J. Bensoussan, S. Cotin, C. Duriez,*
00022 * H. Delingette, F. Falipou, F. Faure, S. Fonteneau, L. Heigeas, C. Mendoza,  *
00023 * M. Nesme, P. Neumann, J-P. de la Plata Alcade, F. Poyer and F. Roy          *
00024 *                                                                             *
00025 * Contact information: contact@sofa-framework.org                             *
00026 ******************************************************************************/
00027 /*
00028  *          polygon_intersects_cube()
00029  *          by Don Hatch
00030  *          January 1994
00031  *
00032  *   Algorithm:
00033  *        1. If any edge intersects the cube, return true.
00034  *           Testing whether a line segment intersects the cube
00035  *           is equivalent to testing whether the origin is contained
00036  *           in the rhombic dodecahedron obtained by dragging
00037  *           a unit cube from (being centered at) one segment endpoint
00038  *           to the other.
00039  *        2. If the polygon interior intersects the cube, return true.
00040  *           Since we know no vertex or edge intersects the cube,
00041  *           this amounts to testing whether any of the four cube diagonals
00042  *           intersects the interior of the polygon.  (Same as voorhies's test).
00043  *        3. Return false.
00044  */
00045 
00046 #include "polygon_cube_intersection.h"
00047 #include "vec.h"
00048 
00049 namespace sofa
00050 {
00051 namespace helper
00052 {
00053 namespace polygon_cube_intersection
00054 {
00055 
00056 #define FOR(i,n) for ((i) = 0; (i) < (n); ++(i))
00057 #define MAXDIM2(v) ((v)[0] > (v)[1] ? 0 : 1)
00058 #define MAXDIM3(v) ((v)[0] > (v)[2] ? MAXDIM2(v) : MAXDIM2((v)+1)+1)
00059 #define ABS(x) ((x)<0 ? -(x) : (x))
00060 #define SQR(x) ((x)*(x))
00061 #define SIGN_NONZERO(x) ((x) < 0 ? -1 : 1)
00062 /* note a and b can be in the reverse order and it still works! */
00063 #define IN_CLOSED_INTERVAL(a,x,b) (((x)-(a)) * ((x)-(b)) <= 0)
00064 #define IN_OPEN_INTERVAL(a,x,b) (((x)-(a)) * ((x)-(b)) < 0)
00065 
00066 
00067 
00068 #define seg_contains_point(a,b,x) (((b)>(x)) - ((a)>(x)))
00069 /*
00070  *  Tells whether a given polygon with nonzero area
00071  *  contains a point which is assumed to lie in the plane of the polygon.
00072  *  Actually returns the multiplicity of containment.
00073  *  This will always be 1 or 0 for non-self-intersecting planar
00074  *  polygons with the normal in the standard direction
00075  *  (towards the eye when looking at the polygon so that it's CCW).
00076  */
00077 extern int
00078 polygon_contains_point_3d(int nverts, const float verts[/* nverts */][3],
00079             const float polynormal[3],
00080             float point[3])
00081 {
00082     float abspolynormal[3];
00083     int zaxis, xaxis, yaxis, i, count;
00084     int xdirection;
00085     const float *v, *w;
00086 
00087     /*
00088      * Determine which axis to ignore
00089      * (the one in which the polygon normal is largest)
00090      */
00091     FOR(i,3)
00092     abspolynormal[i] = ABS(polynormal[i]);
00093     zaxis = MAXDIM3(abspolynormal);
00094 
00095     if (polynormal[zaxis] < 0) {
00096     xaxis = (zaxis+2)%3;
00097     yaxis = (zaxis+1)%3;
00098     } else {
00099     xaxis = (zaxis+1)%3;
00100     yaxis = (zaxis+2)%3;
00101     }
00102 
00103     count = 0;
00104     FOR(i,nverts) {
00105     v = verts[i];
00106     w = verts[(i+1)%nverts];
00107     if ((xdirection = seg_contains_point(v[xaxis], w[xaxis], point[xaxis]))) {
00108         if (seg_contains_point(v[yaxis], w[yaxis], point[yaxis])) {
00109         if (xdirection * (point[xaxis]-v[xaxis])*(w[yaxis]-v[yaxis]) <= 
00110             xdirection * (point[yaxis]-v[yaxis])*(w[xaxis]-v[xaxis]))
00111             count += xdirection;
00112         } else {
00113         if (v[yaxis] <= point[yaxis])
00114             count += xdirection;
00115         }
00116     }
00117     }
00118     return count;
00119 }
00120 
00121 
00122 
00123 
00124 /*
00125  *  A segment intersects the unit cube centered at the origin
00126  *  iff the origin is contained in the solid obtained
00127  *  by dragging a unit cube from one segment endpoint to the other.
00128  *  (This solid is a warped rhombic dodecahedron.)
00129  *  This amounts to 12 sidedness tests.
00130  *  Also, this test works even if one or both of the segment endpoints is
00131  *  inside the cube.
00132  */
00133 extern int
00134 segment_intersects_cube(const float v0[3], const float v1[3])
00135 {
00136     int i, iplus1, iplus2, edgevec_signs[3];
00137     float edgevec[3];
00138 
00139     VMV3(edgevec, v1, v0);
00140 
00141     FOR(i,3)
00142     edgevec_signs[i] = SIGN_NONZERO(edgevec[i]);
00143 
00144     /*
00145      * Test the three cube faces on the v1-ward side of the cube--
00146      * if v0 is outside any of their planes then there is no intersection.
00147      * Also test the three cube faces on the v0-ward side of the cube--
00148      * if v1 is outside any of their planes then there is no intersection.
00149      */
00150 
00151     FOR(i,3) {
00152     if (v0[i] * edgevec_signs[i] >  .5) return 0;
00153     if (v1[i] * edgevec_signs[i] < -.5) return 0;
00154     }
00155 
00156     /*
00157      * Okay, that's the six easy faces of the rhombic dodecahedron
00158      * out of the way.  Six more to go.
00159      * The remaining six planes bound an infinite hexagonal prism
00160      * joining the petrie polygons (skew hexagons) of the two cubes
00161      * centered at the endpoints.
00162      */
00163 
00164     FOR(i,3) {
00165     float rhomb_normal_dot_v0, rhomb_normal_dot_cubedge;
00166 
00167     iplus1 = (i+1)%3;
00168     iplus2 = (i+2)%3;
00169 
00170 #ifdef THE_EASY_TO_UNDERSTAND_WAY
00171 
00172     {
00173     float rhomb_normal[3], cubedge_midpoint[3];
00174 
00175     /*
00176      * rhomb_normal = VXV3(edgevec, unit vector in direction i),
00177      * being cavalier about which direction it's facing
00178      */
00179     rhomb_normal[i] = 0;
00180     rhomb_normal[iplus1] = edgevec[iplus2];
00181     rhomb_normal[iplus2] = -edgevec[iplus1];
00182 
00183     /*
00184      *  We now are describing a plane parallel to
00185      *  both segment and the cube edge in question.
00186      *  if |DOT3(rhomb_normal, an arbitrary point on the segment)| >
00187      *  |DOT3(rhomb_normal, an arbitrary point on the cube edge in question|
00188      *  then the origin is outside this pair of opposite faces.
00189      *  (This is equivalent to saying that the line
00190      *  containing the segment is "outside" (i.e. further away from the
00191      *  origin than) the line containing the cube edge.
00192      */
00193 
00194     cubedge_midpoint[i] = 0;
00195     cubedge_midpoint[iplus1] = edgevec_signs[iplus1]*.5;
00196     cubedge_midpoint[iplus2] = -edgevec_signs[iplus2]*.5;
00197 
00198     rhomb_normal_dot_v0 = DOT3(rhomb_normal, v0);
00199     rhomb_normal_dot_cubedge = DOT3(rhomb_normal,cubedge_midpoint);
00200     }
00201 
00202 #else /* the efficient way */
00203 
00204     rhomb_normal_dot_v0 = edgevec[iplus2] * v0[iplus1]
00205                 - edgevec[iplus1] * v0[iplus2];
00206 
00207     rhomb_normal_dot_cubedge = .5f *
00208                 (edgevec[iplus2] * edgevec_signs[iplus1] +
00209                  edgevec[iplus1] * edgevec_signs[iplus2]);
00210 
00211 #endif /* the efficient way */
00212 
00213     if (SQR(rhomb_normal_dot_v0) > SQR(rhomb_normal_dot_cubedge))
00214         return 0;   /* origin is outside this pair of opposite planes */
00215     }
00216     return 1;
00217 }
00218 
00219 
00220 
00221 
00222 
00223 /*
00224  * Tells whether a given polygon intersects the cube of edge length 1
00225  * centered at the origin.
00226  * Always returns 1 if a polygon edge intersects the cube;
00227  * returns the multiplicity of containment otherwise.
00228  * (See explanation of polygon_contains_point_3d() above).
00229  */
00230 extern int
00231 polygon_intersects_cube(int nverts, const float verts[/* nverts */][3],
00232             const float polynormal[3],
00233             int ,/* already_know_vertices_are_outside_cube unused*/
00234             int already_know_edges_are_outside_cube)
00235 {
00236     int i, best_diagonal[3];
00237     float p[3], t;
00238 
00239     /*
00240      * If any edge intersects the cube, return 1.
00241      */
00242     if (!already_know_edges_are_outside_cube)
00243     FOR(i,nverts)
00244         if (segment_intersects_cube(verts[i], verts[(i+1)%nverts]))
00245         return 1;
00246 
00247     /*
00248      * If the polygon normal is zero and none of its edges intersect the
00249      * cube, then it doesn't intersect the cube
00250      */
00251     if (ISZEROVEC3(polynormal))
00252     return 0;
00253 
00254     /*
00255      * Now that we know that none of the polygon's edges intersects the cube,
00256      * deciding whether the polygon intersects the cube amounts
00257      * to testing whether any of the four cube diagonals intersects
00258      * the interior of the polygon.
00259      *
00260      * Notice that we only need to consider the cube diagonal that comes
00261      * closest to being perpendicular to the plane of the polygon.
00262      * If the polygon intersects any of the cube diagonals,
00263      * it will intersect that one.
00264      */
00265 
00266     FOR(i,3)
00267     best_diagonal[i] = SIGN_NONZERO(polynormal[i]);
00268 
00269     /*
00270      * Okay, we have the diagonal of interest.
00271      * The plane containing the polygon is the set of all points p satisfying
00272      *      DOT3(polynormal, p) == DOT3(polynormal, verts[0])
00273      * So find the point p on the cube diagonal of interest
00274      * that satisfies this equation.
00275      * The line containing the cube diagonal is described parametrically by
00276      *      t * best_diagonal
00277      * so plug this into the previous equation and solve for t.
00278      *      DOT3(polynormal, t * best_diagonal) == DOT3(polynormal, verts[0])
00279      * i.e.
00280      *      t = DOT3(polynormal, verts[0]) / DOT3(polynormal, best_diagonal)
00281      *
00282      * (Note that the denominator is guaranteed to be nonzero, since
00283      * polynormal is nonzero and best_diagonal was chosen to have the largest
00284      * magnitude dot-product with polynormal)
00285      */
00286     t = DOT3(polynormal, verts[0])
00287       / DOT3(polynormal, best_diagonal);
00288 
00289     if (!IN_CLOSED_INTERVAL(-.5, t, .5))
00290     return 0;  /* intersection point is not in cube */
00291 
00292     SXV3(p, t, best_diagonal);    /* p = t * best_diagonal */
00293 
00294     return polygon_contains_point_3d(nverts, verts, polynormal, p);
00295 }
00296 
00297 extern float *
00298 get_polygon_normal(float normal[3],
00299            int nverts, const float verts[/* nverts */][3])
00300 {
00301     int i;
00302     float tothis[3], toprev[3], cross[3];
00303 
00304     /*
00305      * Triangulate the polygon and sum up the nverts-2 triangle normals.
00306      */
00307     ZEROVEC3(normal);
00308     VMV3(toprev, verts[1], verts[0]);   /* 3 subtracts */
00309     for (i = 2; i <= nverts-1; ++i) {   /* n-2 times... */
00310     VMV3(tothis, verts[i], verts[0]);    /* 3 subtracts */
00311     VXV3(cross, toprev, tothis);         /* 3 subtracts, 6 multiplies */
00312     VPV3(normal, normal, cross);         /* 3 adds */
00313     SET3(toprev, tothis);
00314     }
00315     return normal;
00316 }
00317 
00318 }
00319 }
00320 }

Generated on Sun Nov 23 02:29:30 2008 for Sofa by  doxygen 1.5.7.1