CompGeom.h

00001 /****************************************************************************
00002  * Copyright 2006 Evan Drumwright
00003  * This library is distributed under the terms of the GNU General Public 
00004  * License (found in COPYING).
00005  ****************************************************************************/
00006 
00007 #ifndef _COMPGEOM_H
00008 #define _COMPGEOM_H
00009 
00010 #include <pthread.h>
00011 #include <iostream>
00012 #include <vector>
00013 #include <map>
00014 #include <list>
00015 #include <boost/shared_ptr.hpp>
00016 #include <Physsim/Constants.h>
00017 #include <Physsim/Triangle.h>
00018 #include <Physsim/Vector2.h>
00019 #include <Physsim/Polyhedron.h>
00020 
00021 // needed for qhull
00022 extern "C"
00023 {
00024         #include <qhull/qhull.h>
00025         #include <qhull/qset.h>
00026         #include <qhull/user.h>
00027 }
00028 
00029 namespace Physsim {
00030 
00031 class Polyhedron;       
00032         
00034 class CompGeom
00035 {
00036         public:
00037                 enum SegSegIntersectType { SegSegNoIntersect, SegSegIntersect, SegSegVertex, SegSegEdge };
00038                 enum SegTriIntersectType { SegTriNoIntersect, SegTriInclFace, SegTriInclVertex, SegTriInclEdge, SegTriInside, SegTriVertex, SegTriEdge, SegTriFace, SegTriEdgeOverlap, SegTriPlanarIntersect };
00039                 enum SegPlaneIntersectType { SegPlaneInPlane, SegPlaneToSide, SegPlaneFirst, SegPlaneSecond, SegPlaneOtherIntersect };
00040                 enum PolygonLocation { PolygonInside, PolygonOutside, PolygonOnVertex, PolygonOnEdge };
00041                 enum SegBoxIntersectType { SegBoxNoIntersect, SegBoxFullyInside, SegBoxPartiallyInside, SegBoxSurfaceIntersect, SegBoxPointIntersect, SegBoxPassThrough };      
00042 
00043                 static Vector3 generate_point_on_plane(const Vector3& normal, Real d);
00044                 static SegBoxIntersectType intersect_seg_box(const Vector3& lo, const Vector3& hi, const std::pair<Vector3, Vector3>& seg, Vector3& isect1, Vector3& isect2);
00045                 static bool is_convex_polygon(const std::list<Vector3>& polygon, const Vector3& normal);
00046                 static std::list<TrianglePtr> triangulate_convex_polygon(const std::list<Vector3>& polygon);
00047                 static bool intersect_noncoplanar_tris(const Triangle& t1, const Triangle& t2, Vector3& p1, Vector3& p2);
00048                 static boost::shared_ptr<std::list<Vector3> > intersect_tris(const Triangle& t1, const Triangle& t2);
00049                 static Real fit_plane(const std::list<Vector3>& points, Vector3& normal, Real& d);
00050                 static SegTriIntersectType intersect_seg_tri(const Triangle& t, const std::pair<Vector3, Vector3>& seg, Vector3& isect, Vector3& isect2);
00051                 static SegSegIntersectType intersect_segs_3D(const std::pair<Vector3, Vector3>& s1, const std::pair<Vector3, Vector3>& s2, Vector3& isect, Vector3& isect2);
00052                 static SegPlaneIntersectType intersect_seg_plane(const Triangle& tri, const std::pair<Vector3, Vector3>& seg, Vector3& isect, unsigned int& big_dim);
00053                 static boost::shared_ptr<std::list<Vector3> > calc_convex_hull_2D(const std::list<Vector3>& points, const VectorN& normal);
00054                 static PolyhedronPtr calc_convex_hull_3D(const std::list<Vector3>& points);
00055                 static boost::shared_ptr<std::list<Vector3> > intersect_coplanar_tris(const Triangle& t1, const Triangle& t2, const Vector3& normal, Real d);
00056                 static SegSegIntersectType intersect_segs_2D(const std::pair<Vector2, Vector2>& s1, const std::pair<Vector2, Vector2>& s2, Vector2& isect, Vector2& isect2);
00057                 static PolygonLocation polygon_location(const std::list<Vector2>& polygon, const Vector2& point);
00058                 static bool collinear(const Vector3& a, const Vector3& b, const Vector3& c);
00059                 static bool collinear(const Vector2& a, const Vector2& b, const Vector2& c);
00060                 static LongReal volume(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d);
00061                 static int volume_sign(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d);
00062                 static Vector3 calc_centroid_2D(const std::list<Vector3>& poly, const Vector3& normal);
00063                 static void project_plane(std::list<Vector3>& points, const Vector3& normal, Real d);
00064                 static bool coplanar(const Triangle& t1, const Triangle& t2);
00065                 static Real calc_area(const std::list<Vector3>& poly, const Vector3& normal);   
00066                 static Vector3 to_3D(const Vector2& v);
00067                 static Vector3 to_3D(const Vector2& v, Real d);
00068                 static Vector2 to_2D(const Vector3& v);
00069 
00070                 static void det_line_endpoints(std::list<Vector3>& points, std::pair<Vector3, Vector3>& endpoints);
00071                 static void det_line_endpoints(std::list<Vector2>& points, std::pair<Vector2, Vector2>& endpoints);
00072                 static bool query_intersect_tri_tri(const Triangle& t1, const Triangle& t2);
00073 
00074                 template <class ForwardIterator>
00075                 static bool collinear(ForwardIterator first, ForwardIterator last);
00076 
00077                 template <class ForwardIterator>
00078                 static PolyhedronPtr calc_convex_hull_3D(ForwardIterator first, ForwardIterator last);
00079 
00080                 template <class ForwardIterator>
00081                 static Vector3 calc_centroid(ForwardIterator first, ForwardIterator last);
00082                 
00083         private:
00084                 static pthread_mutex_t _qhull_mutex;
00085                 static bool compute_intervals_isectline(const Triangle& t1, Real vv0, Real vv1, Real vv2, Real d0, Real d1, Real d2, Real d0d1, Real d0d2, Real& isect0, Real& isect1, Vector3& isectpoint0, Vector3& isectpoint1);
00086                 static void isect2X(const Vector3& vtx0, const Vector3& vtx1, const Vector3& vtx2, Real vv0, Real vv1, Real vv2, Real d0, Real d1, Real d2, Real& isect0, Real& isect1, Vector3& isectpoint0, Vector3& isectpoint1);
00087                 static SegSegIntersectType get_parallel_intersect_type(const std::pair<Vector2, Vector2>& s1, const std::pair<Vector2, Vector2>& s2, Vector2& isect, Vector2& isect2);
00088                 static bool between(const Vector2& a, const Vector2& b, const Vector2& c);
00089                 static unsigned advance(unsigned a, unsigned* aa, unsigned n, bool inside, const Vector2& v, std::list<Vector3>& poly, const Matrix3& RT, Real d);
00090                 static SegTriIntersectType seg_tri_cross(const Triangle& t, const Vector3& q, const Vector3& r);
00091                 static SegTriIntersectType in_tri_3D(const Triangle& t, const Vector3& p, unsigned int skip_dim);
00092                 static SegTriIntersectType in_tri_2D(const Vector2& ta, const Vector2& tb, const Vector2& tc, const Vector2& p);
00093                 static void find_plane_coeff(const Triangle& t, Vector3& N, unsigned int& m, Real& dot);
00094                 static VectorN normal_vec(const Vector3& a, const Vector3& b, const Vector3& c);
00095                 static int area_sign(const Vector2& a, const Vector2& b, const Vector2& c);
00096                 static bool test_edge_edge(const Vector3& p, const Vector3& a, const Vector3& b, Real Ax, Real Ay, unsigned i0, unsigned i1);
00097                 static bool test_edge_tri(const Vector3& a, const Vector3& b, const Triangle& t, unsigned i0, unsigned i1);
00098                 static bool test_point_in_tri(const Vector3& p, const Triangle& t, unsigned i0, unsigned i1);
00099                 static bool test_coplanar_tri_tri(const Vector3& N, const Triangle& t1, const Triangle& t2);
00100 }; // end class
00101 
00102 /**************************************************************
00103  * Template methods follow
00104  *************************************************************/
00105 
00107 template <class ForwardIterator>
00108 bool CompGeom::collinear(ForwardIterator first, ForwardIterator last)
00109 {  
00110         for (ForwardIterator i = first; i != last; i++)
00111         {
00112                 ForwardIterator j = i;
00113                 j++;
00114                 for (; j != last; j++)
00115                 {
00116                         ForwardIterator k = j;
00117                         k++;
00118                         for (; k != last; k++)
00119                                 if (!collinear(*i, *j, *k))
00120                                         return false;
00121                 }
00122         }
00123         
00124         return true;
00125 }
00126 
00128 
00131 template <class ForwardIterator>
00132 Vector3 CompGeom::calc_centroid(ForwardIterator first, ForwardIterator last)
00133 {
00134         Real area_sum = 0;
00135         
00136         // compute the area of each facet and contribution from each facet
00137         Vector3 centroid = ZEROS_3;
00138         for (ForwardIterator i = first; i != last; i++)
00139         {
00140                 Real area = (*i)->get_area();
00141                 area_sum += area;
00142                 centroid += area * (*(*i)->a() + *(*i)->b() + *(*i)->c());
00143         }
00144         
00145         // divide the centroid by 3*area_sum
00146         centroid /= (area_sum * 3);
00147         
00148         return centroid;
00149 }
00150 
00152 
00157 template <class ForwardIterator>
00158 PolyhedronPtr CompGeom::calc_convex_hull_3D(ForwardIterator first, ForwardIterator last)
00159 {
00160         const unsigned X = 0, Y = 1, Z = 2;
00161         FILE* outfile=NULL;
00162         FILE* errfile=stderr;
00163         int exit_code;
00164         int curlong, totlong;
00165 
00166         // setup qhull output (standard -- error always goes to stderr)
00167         #ifdef _DEBUG_COMPGEOM_
00168         outfile = stdout;
00169         #endif
00170 
00171         // determine how many points we are processing
00172         unsigned sz = 0;
00173         for (ForwardIterator i = first; i != last; i++)
00174                 sz++;
00175 
00176         // setup constants
00177         const int DIM = 3;
00178         const int N_POINTS = sz;
00179         const boolT IS_MALLOC = false;
00180         char flags[] = "qhull ";
00181 
00182         // make sure there are enough points
00183         assert(N_POINTS >= 4);
00184         
00185         // setup the points
00186         coordT* qhull_points = new coordT[N_POINTS*DIM];
00187         unsigned j=0;
00188         for (ForwardIterator i = first; i != last; i++)
00189         {
00190                 qhull_points[j] = (*i)[X];
00191                 qhull_points[j+1] = (*i)[Y];
00192                 qhull_points[j+2] = (*i)[Z];
00193                 j += DIM;
00194         }
00195         
00196         #ifdef _DEBUG_COMPGEOM_
00197         std::cout << "computing 3D convex hull of: " << std::endl;
00198         for (ForwardIterator i = first; i != last; i++)
00199                 std::cout << *i << std::endl;
00200         #endif
00201 
00202         // lock the qhull mutex -- qhull is non-reentrant
00203         pthread_mutex_lock(&_qhull_mutex);
00204         
00205         // execute qhull        
00206         exit_code = qh_new_qhull(DIM, N_POINTS, qhull_points, IS_MALLOC, flags, outfile, errfile);
00207         if (exit_code)
00208         {
00209                 // qhull failed -- perhaps the dimensionality is 2 rather than 3?
00210                 pthread_mutex_unlock(&_qhull_mutex);
00211                 return PolyhedronPtr();
00212         }
00213 
00214         // construct a new mesh from the facets
00215         std::vector<TriangleConstPtr> mesh;
00216 
00217         // get all of the vertices
00218         std::map<vertexT*, Vector3Ptr > vertices;
00219         for (vertexT* vertex=qh vertex_list;vertex && vertex->next;vertex= vertex->next)
00220         {
00221                 Vector3Ptr v(new Vector3);
00222                 for (unsigned i=0; i< (unsigned) DIM; i++)
00223                         (*v)[i] = vertex->point[i];
00224                 vertices[vertex] = v;
00225         }
00226         
00227         // get the facet information
00228         for (facetT* facet=qh facet_list;facet && facet->next;facet=facet->next)
00229         {
00230                 if (!facet->vertices)
00231                         continue;
00232 
00233                 // setup an array of vertices for the facet
00234                 std::vector<Vector3Ptr> facet_vertices;
00235                 
00236                 // get all vertices in the facet
00237                 vertexT* vertex;
00238                 for (vertexT** vertex_pointer = (vertexT**)& ((facet->vertices)->e[0].p); (vertex = (*vertex_pointer++));)
00239                         facet_vertices.push_back(vertices[vertex]);
00240 
00241                 // triangulate the facet; try to use only the original edges first
00242                 bool colin = false;
00243                 std::list<TrianglePtr > new_tris;
00244                 for (unsigned i=1; i< facet_vertices.size()-1; i++)
00245                 {
00246                         // check whether the the three points are collinear
00247                         if (collinear(*facet_vertices[i], *facet_vertices[i+1], *facet_vertices.front()))
00248                         {
00249                                 colin = true;
00250                                 new_tris.clear();
00251                                 break;
00252                         }
00253                         new_tris.push_back(TrianglePtr(new Triangle(facet_vertices[i], facet_vertices[i+1], facet_vertices.front())));
00254                 }       
00255                 
00256                 // if it is collinear, create a new point at the centroid of the points
00257                 if (colin)
00258                 {
00259                         // compute the centroid
00260                         Vector3Ptr centroid(new Vector3(ZEROS_3));
00261                         for (unsigned i=0; i< facet_vertices.size(); i++)
00262                                 (*centroid) += *facet_vertices[i];
00263                         (*centroid) /= facet_vertices.size();
00264                         
00265                         // now make the new triangles -- first, add the first vertex twice for closure of the polygon
00266                         facet_vertices.push_back(facet_vertices.front());
00267                         for (unsigned i=0; i< facet_vertices.size()-1; i++)
00268                                 new_tris.push_back(TrianglePtr(new Triangle(facet_vertices[i], facet_vertices[i+1], centroid)));
00269                 }
00270                 
00271                 // get one normal
00272                 Vector3 normal = new_tris.front()->normal();
00273                 
00274                 // determine whether the normal is inverted; if so, reverse ordering on all triangles
00275                 Vector3 true_normal(facet->normal[X], facet->normal[Y], facet->normal[Z]);
00276                 if (Vector3::dot(normal, true_normal) < 0)
00277                         for (std::list<TrianglePtr >::iterator i = new_tris.begin(); i != new_tris.end(); i++)
00278                         {
00279                                 Vector3ConstPtr a = (*i)->a();
00280                                 (*i)->set_a((*i)->b());
00281                                 (*i)->set_b(a);
00282                         }
00283                         
00284                 // add all triangles to the mesh
00285                 while (!new_tris.empty())
00286                 {
00287                         mesh.push_back(new_tris.front());
00288                         new_tris.pop_front();
00289                 }
00290         }
00291         
00292         // free memory for the points
00293         delete [] qhull_points;
00294         
00295         // free qhull memory
00296         qh_freeqhull(!qh_ALL);
00297         qh_memfreeshort(&curlong, &totlong);
00298         assert(!curlong && !totlong);
00299         
00300         // release the qhull mutex
00301         pthread_mutex_unlock(&_qhull_mutex);
00302         
00303         // if the there aren't enough triangles, can't create the polyhedron
00304         assert(mesh.size() >= 4);
00305         
00306         // create the polyhedron
00307         PolyhedronPtr polyhedron(new Polyhedron(mesh));
00308 /*
00309         #ifndef NDEBUG
00310         if (!polyhedron->is_convex())
00311         {
00312                 std::ofstream out_nc("non_convex.wrl");
00313                 out_nc << "#VRML V2.0 utf8" << std::endl;
00314                 Polyhedron::to_VRML(out_nc, *polyhedron, Vector3(1,0,0), false); 
00315                 out_nc.close();
00316                 assert(false);  
00317         }
00318         #endif
00319 */      
00320         #ifdef _DEBUG_COMPGEOM_
00321         std::cout << "3D convex hull is:" << std::endl << *polyhedron;
00322         #endif
00323 
00324         return polyhedron;      
00325 }
00326 
00327 } // end namespace
00328 
00329 #endif

Generated on Wed Oct 24 14:54:21 2007 for Physsim by  doxygen 1.5.1