glc_geomtools.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 
00003  This file is part of the GLC-lib library.
00004  Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
00005  http://glc-lib.sourceforge.net
00006 
00007  GLC-lib is free software; you can redistribute it and/or modify
00008  it under the terms of the GNU Lesser General Public License as published by
00009  the Free Software Foundation; either version 3 of the License, or
00010  (at your option) any later version.
00011 
00012  GLC-lib is distributed in the hope that it will be useful,
00013  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  GNU Lesser General Public License for more details.
00016 
00017  You should have received a copy of the GNU Lesser General Public License
00018  along with GLC-lib; if not, write to the Free Software
00019  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 *****************************************************************************/
00022 
00024 
00025 #include "glc_geomtools.h"
00026 #include "glc_matrix4x4.h"
00027 
00028 #include <QtGlobal>
00029 
00031 //Tools Functions
00033 
00034 // Test if a polygon is convex
00035 bool glc::polygon2DIsConvex(const QList<GLC_Point2d>& vertices)
00036 {
00037         bool isConvex= true;
00038         const int size= vertices.size();
00039         if (vertices.size() > 3)
00040         {
00041                 GLC_Point2d s0(vertices.at(0));
00042                 GLC_Point2d s1(vertices.at(1));
00043                 GLC_Point2d s2(vertices.at(2));
00044                 const bool openAngle= ((s1.getX() - s0.getX()) * (s2.getY() - s0.getY()) - (s2.getX() - s0.getX()) * (s1.getY() - s0.getY())) < 0.0;
00045 
00046                 int i= 3;
00047                 while ((i < size) && isConvex)
00048                 {
00049                         s0= s1;
00050                         s1= s2;
00051                         s2= vertices.at(i);
00052                         isConvex= openAngle == (((s1.getX() - s0.getX()) * (s2.getY() - s0.getY()) - (s2.getX() - s0.getX()) * (s1.getY() - s0.getY())) < 0.0);
00053                         ++i;
00054                 }
00055         }
00056 
00057         return isConvex;
00058 }
00059 
00060 bool glc::polygonIsConvex(QList<GLuint>* pIndexList, const QList<float>& bulkList)
00061 {
00062         bool isConvex= true;
00063         const int size= pIndexList->size();
00064         GLuint currentIndex;
00065         GLC_Vector3d v0;
00066         GLC_Vector3d v1;
00067         int i= 0;
00068         while ((i < size) && isConvex)
00069         {
00070                 currentIndex= pIndexList->at(i);
00071                 v0.setVect(bulkList.at(currentIndex * 3), bulkList.at(currentIndex * 3 + 1), bulkList.at(currentIndex * 3 + 2));
00072                 currentIndex= pIndexList->at((i + 1) % size);
00073                 v1.setVect(bulkList.at(currentIndex * 3), bulkList.at(currentIndex * 3 + 1), bulkList.at(currentIndex * 3 + 2));
00074                 isConvex= (v0.angleWithVect(v1) < glc::PI);
00075                 ++i;
00076         }
00077         return isConvex;
00078 }
00079 // find intersection between two 2D segments
00080 QVector<GLC_Point2d> glc::findIntersection(const GLC_Point2d& s1p1, const GLC_Point2d& s1p2, const GLC_Point2d& s2p1, const GLC_Point2d& s2p2)
00081 {
00082         const GLC_Vector2d D0= s1p2 - s1p1;
00083         const GLC_Vector2d D1= s2p2 - s2p1;
00084         // The QVector of result points
00085         QVector<GLC_Point2d> result;
00086 
00087         const GLC_Vector2d E(s2p1 - s1p1);
00088         double kross= D0 ^ D1;
00089         double sqrKross= kross * kross;
00090         const double sqrLen0= D0.getX() * D0.getX() + D0.getY() * D0.getY();
00091         const double sqrLen1= D1.getX() * D1.getX() + D1.getY() * D1.getY();
00092         // Test if the line are nor parallel
00093         if (sqrKross > (EPSILON * sqrLen0 * sqrLen1))
00094         {
00095                 const double s= (E ^ D1) / kross;
00096                 if ((s < 0.0) || (s > 1.0))
00097                 {
00098                         // Intersection of lines is not a point on segment s1p1 + s * DO
00099                         return result; // Return empty QVector
00100                 }
00101                 const double t= (E ^ D0) / kross;
00102 
00103                 if ((t < 0.0) || (t > 1.0))
00104                 {
00105                         // Intersection of lines is not a point on segment s2p1 + t * D1
00106                         return result; // Return empty QVector
00107                 }
00108 
00109                 // Intersection of lines is a point on each segment
00110                 result << (s1p1 + (D0 * s));
00111                 return result; // Return a QVector of One Point
00112         }
00113 
00114         // Lines of the segments are parallel
00115         const double sqrLenE= E.getX() * E.getX() + E.getY() * E.getY();
00116         kross= E ^ D0;
00117         sqrKross= kross * kross;
00118         if (sqrKross > (EPSILON * sqrLen0 * sqrLenE))
00119         {
00120                 // Lines of the segments are different
00121                 return result; // Return empty QVector
00122         }
00123 
00124         // Lines of the segments are the same. Need to test for overlap of segments.
00125         const double s0= (D0 * E) / sqrLen0;
00126         const double s1= (D0 * D1) / sqrLen0;
00127         const double sMin= qMin(s0, s1);
00128         const double sMax= qMax(s0, s1);
00129         QVector<double> overlaps= findIntersection(0.0, 1.0, sMin, sMax);
00130         const int iMax= overlaps.size();
00131         for (int i= 0; i < iMax; ++i)
00132         {
00133                 result.append(s1p1 + (D0 * overlaps[i]));
00134         }
00135         return result;
00136 }
00137 
00138 // return true if there is an intersection between 2 segments
00139 bool glc::isIntersected(const GLC_Point2d& s1p1, const GLC_Point2d& s1p2, const GLC_Point2d& s2p1, const GLC_Point2d& s2p2)
00140 {
00141         const GLC_Vector2d D0= s1p2 - s1p1;
00142         const GLC_Vector2d D1= s2p2 - s2p1;
00143 
00144         const GLC_Vector2d E(s2p1 - s1p1);
00145         double kross= D0 ^ D1;
00146         double sqrKross= kross * kross;
00147         const double sqrLen0= D0.getX() * D0.getX() + D0.getY() * D0.getY();
00148         const double sqrLen1= D1.getX() * D1.getX() + D1.getY() * D1.getY();
00149         // Test if the line are nor parallel
00150         if (sqrKross > (EPSILON * sqrLen0 * sqrLen1))
00151         {
00152                 const double s= (E ^ D1) / kross;
00153                 if ((s < 0.0) || (s > 1.0))
00154                 {
00155                         // Intersection of lines is not a point on segment s1p1 + s * DO
00156                         return false;
00157                 }
00158                 const double t= (E ^ D0) / kross;
00159 
00160                 if ((t < 0.0) || (t > 1.0))
00161                 {
00162                         // Intersection of lines is not a point on segment s2p1 + t * D1
00163                         return false;
00164                 }
00165 
00166                 // Intersection of lines is a point on each segment
00167                 return true;
00168         }
00169 
00170         // Lines of the segments are parallel
00171         const double sqrLenE= E.getX() * E.getX() + E.getY() * E.getY();
00172         kross= E ^ D0;
00173         sqrKross= kross * kross;
00174         if (sqrKross > (EPSILON * sqrLen0 * sqrLenE))
00175         {
00176                 // Lines of the segments are different
00177                 return false;
00178         }
00179 
00180         // Lines of the segments are the same. Need to test for overlap of segments.
00181         const double s0= (D0 * E) / sqrLen0;
00182         const double s1= s0 + (D0 * D1) / sqrLen0;
00183         const double sMin= qMin(s0, s1);
00184         const double sMax= qMax(s0, s1);
00185         if (findIntersection(0.0, 1.0, sMin, sMax).size() == 0) return false; else return true;
00186 
00187 }
00188 
00189 // return true if there is an intersection between a ray and a segment
00190 bool glc::isIntersectedRaySegment(const GLC_Point2d& s1p1, const GLC_Vector2d& s1p2, const GLC_Point2d& s2p1, const GLC_Point2d& s2p2)
00191 {
00192         const GLC_Vector2d D0= s1p2 - s1p1;
00193         const GLC_Vector2d D1= s2p2 - s2p1;
00194 
00195         const GLC_Vector2d E(s2p1 - s1p1);
00196         double kross= D0 ^ D1;
00197         double sqrKross= kross * kross;
00198         const double sqrLen0= D0.getX() * D0.getX() + D0.getY() * D0.getY();
00199         const double sqrLen1= D1.getX() * D1.getX() + D1.getY() * D1.getY();
00200         // Test if the line are nor parallel
00201         if (sqrKross > (EPSILON * sqrLen0 * sqrLen1))
00202         {
00203                 const double s= (E ^ D1) / kross;
00204                 if ((s < 0.0))
00205                 {
00206                         // Intersection of lines is not a point on segment s1p1 + s * DO
00207                         return false;
00208                 }
00209                 const double t= (E ^ D0) / kross;
00210 
00211                 if ((t < 0.0) || (t > 1.0))
00212                 {
00213                         // Intersection of lines is not a point on segment s2p1 + t * D1
00214                         return false;
00215                 }
00216 
00217                 // Intersection of lines is a point on each segment
00218                 return true;
00219         }
00220 
00221         // Lines of the segments are parallel
00222         const double sqrLenE= E.getX() * E.getX() + E.getY() * E.getY();
00223         kross= E ^ D0;
00224         sqrKross= kross * kross;
00225         if (sqrKross > (EPSILON * sqrLen0 * sqrLenE))
00226         {
00227                 // Lines of are different
00228                 return false;
00229         }
00230         else return true;
00231 
00232 }
00233 
00234 // find intersection of two intervals [u0, u1] and [v0, v1]
00235 QVector<double> glc::findIntersection(const double& u0, const double& u1, const double& v0, const double& v1)
00236 {
00237         //Q_ASSERT((u0 < u1) and (v0 < v1));
00238         QVector<double> result;
00239         if (u1 < v0 || u0 > v1) return result; // Return empty QVector
00240 
00241         if (u1 > v0)
00242         {
00243                 if (u0 < v1)
00244                 {
00245                         if (u0 < v0) result.append(v0); else result.append(u0);
00246                         if (u1 > v1) result.append(v1); else result.append(u1);
00247                         return result;
00248                 }
00249                 else // u0 == v1
00250                 {
00251                         result.append(u0);
00252                         return result;
00253                 }
00254         }
00255         else // u1 == v0
00256         {
00257                 result.append(u1);
00258                 return result;
00259         }
00260 }
00261 
00262 // return true if the segment is in polygon cone
00263 bool glc::segmentInCone(const GLC_Point2d& V0, const GLC_Point2d& V1, const GLC_Point2d& VM, const GLC_Point2d& VP)
00264 {
00265         // assert: VM, V0, VP are not collinear
00266         const GLC_Vector2d diff(V1 - V0);
00267         const GLC_Vector2d edgeL(VM - V0);
00268         const GLC_Vector2d edgeR(VP - V0);
00269         if ((edgeR ^ edgeL) > 0)
00270         {
00271                 // Vertex is convex
00272                 return (((diff ^ edgeR) < 0.0) && ((diff ^ edgeL) > 0.0));
00273         }
00274         else
00275         {
00276                 // Vertex is reflex
00277                 return (((diff ^ edgeR) < 0.0) || ((diff ^ edgeL) > 0.0));
00278         }
00279 }
00280 
00281 // Return true if the segment is a polygon diagonal
00282 bool glc::isDiagonal(const QList<GLC_Point2d>& polygon, const int i0, const int i1)
00283 {
00284         const int size= polygon.size();
00285         int iM= (i0 - 1) % size;
00286         if (iM < 0) iM= size - 1;
00287         int iP= (i0 + 1) % size;
00288 
00289         if (!segmentInCone(polygon[i0], polygon[i1], polygon[iM], polygon[iP]))
00290         {
00291                 return false;
00292         }
00293 
00294         int j0= 0;
00295         int j1= size - 1;
00296         // test segment <polygon[i0], polygon[i1]> to see if it is a diagonal
00297         while (j0 < size)
00298         {
00299                 if (j0 != i0 && j0 != i1 && j1 != i0 && j1 != i1)
00300                 {
00301                         if (isIntersected(polygon[i0], polygon[i1], polygon[j0], polygon[j1]))
00302                                 return false;
00303                 }
00304 
00305                 j1= j0;
00306                 ++j0;
00307         }
00308 
00309         return true;
00310 }
00311 
00312 // Triangulate a polygon
00313 void glc::triangulate(QList<GLC_Point2d>& polygon, QList<int>& index, QList<int>& tList)
00314 {
00315         const int size= polygon.size();
00316         if (size == 3)
00317         {
00318                 tList << index[0] << index[1] << index[2];
00319                 return;
00320         }
00321         int i0= 0;
00322         int i1= 1;
00323         int i2= 2;
00324         while(i0 < size)
00325         {
00326                 if (isDiagonal(polygon, i0, i2))
00327                 {
00328                         // Add the triangle before removing the ear.
00329                         tList << index[i0] << index[i1] << index[i2];
00330                         // Remove the ear from polygon and index lists
00331                         polygon.removeAt(i1);
00332                         index.removeAt(i1);
00333                         // recurse to the new polygon
00334                         triangulate(polygon, index, tList);
00335                         return;
00336                 }
00337                 ++i0;
00338                 i1= (i1 + 1) % size;
00339                 i2= (i2 + 1) % size;
00340         }
00341 }
00342 
00343 // return true if the polygon is couterclockwise ordered
00344 bool glc::isCounterclockwiseOrdered(const QList<GLC_Point2d>& polygon)
00345 {
00346         const int size= polygon.size();
00347         int j0= 0;
00348         int j1= size - 1;
00349         // test segment <polygon[i0], polygon[i1]> to see if it is a diagonal
00350         while (j0 < size)
00351         {
00352                 GLC_Vector2d perp((polygon[j0] - polygon[j1]).perp());
00353                 int j2= 0;
00354                 int j3= size - 1;
00355                 bool isIntersect= false;
00356                 // Application of perp vector
00357                 GLC_Point2d moy((polygon[j0] + polygon[j1]) * 0.5);
00358                 while (j2 < size && !isIntersect)
00359                 {
00360                         if(j2 != j0 && j3 != j1)
00361                         {
00362                                 if (isIntersectedRaySegment(moy, (perp + moy), polygon[j2], polygon[j3]))
00363                                         isIntersect= true;
00364                         }
00365                         j3= j2;
00366                         ++j2;
00367                 }
00368                 if(!isIntersect) return false;
00369                 j1= j0;
00370                 ++j0;
00371         }
00372 
00373         return true;
00374 
00375 }
00376 
00377 // Triangulate a no convex polygon
00378 void glc::triangulatePolygon(QList<GLuint>* pIndexList, const QList<float>& bulkList)
00379 {
00380         int size= pIndexList->size();
00381         if (polygonIsConvex(pIndexList, bulkList))
00382         {
00383                 QList<GLuint> indexList(*pIndexList);
00384                 pIndexList->clear();
00385                 for (int i= 0; i < size - 2; ++i)
00386                 {
00387                         pIndexList->append(indexList.at(0));
00388                         pIndexList->append(indexList.at(i + 1));
00389                         pIndexList->append(indexList.at(i + 2));
00390                 }
00391         }
00392         else
00393         {
00394                 // Get the polygon vertice
00395                 QList<GLC_Point3d> originPoints;
00396                 QHash<int, int> indexMap;
00397 
00398                 QList<int> face;
00399                 GLC_Point3d currentPoint;
00400                 int delta= 0;
00401                 for (int i= 0; i < size; ++i)
00402                 {
00403                         const int currentIndex= pIndexList->at(i);
00404                         currentPoint= GLC_Point3d(bulkList.at(currentIndex * 3), bulkList.at(currentIndex * 3 + 1), bulkList.at(currentIndex * 3 + 2));
00405                         if (!originPoints.contains(currentPoint))
00406                         {
00407                                 originPoints.append(GLC_Point3d(bulkList.at(currentIndex * 3), bulkList.at(currentIndex * 3 + 1), bulkList.at(currentIndex * 3 + 2)));
00408                                 indexMap.insert(i - delta, currentIndex);
00409                                 face.append(i - delta);
00410                         }
00411                         else
00412                         {
00413                                 qDebug() << "Multi points";
00414                                 ++delta;
00415                         }
00416                 }
00417                 // Values of PindexList must be reset
00418                 pIndexList->clear();
00419 
00420                 // Update size
00421                 size= size - delta;
00422 
00423                 // Check new size
00424                 if (size < 3) return;
00425 
00426                 //-------------- Change frame to mach polygon plane
00427                         // Compute face normal
00428                         const GLC_Point3d point1(originPoints[0]);
00429                         const GLC_Point3d point2(originPoints[1]);
00430                         const GLC_Point3d point3(originPoints[2]);
00431 
00432                         const GLC_Vector3d edge1(point2 - point1);
00433                         const GLC_Vector3d edge2(point3 - point2);
00434 
00435                         GLC_Vector3d polygonPlaneNormal(edge1 ^ edge2);
00436                         polygonPlaneNormal.normalize();
00437 
00438                         // Create the transformation matrix
00439                         GLC_Matrix4x4 transformation;
00440 
00441                         GLC_Vector3d rotationAxis(polygonPlaneNormal ^ Z_AXIS);
00442                         if (!rotationAxis.isNull())
00443                         {
00444                                 const double angle= acos(polygonPlaneNormal * Z_AXIS);
00445                                 transformation.setMatRot(rotationAxis, angle);
00446                         }
00447 
00448                         QList<GLC_Point2d> polygon;
00449                         // Transform polygon vertexs
00450                         for (int i=0; i < size; ++i)
00451                         {
00452                                 originPoints[i]= transformation * originPoints[i];
00453                                 // Create 2d vector
00454                                 polygon << originPoints[i].toVector2d(Z_AXIS);
00455                         }
00456                         // Create the index
00457                         QList<int> index= face;
00458 
00459                         QList<int> tList;
00460                         const bool faceIsCounterclockwise= isCounterclockwiseOrdered(polygon);
00461 
00462                         if(!faceIsCounterclockwise)
00463                         {
00464                                 //qDebug() << "face Is Not Counterclockwise";
00465                                 const int max= size / 2;
00466                                 for (int i= 0; i < max; ++i)
00467                                 {
00468                                         polygon.swap(i, size - 1 -i);
00469                                         int temp= face[i];
00470                                         face[i]= face[size - 1 - i];
00471                                         face[size - 1 - i]= temp;
00472                                 }
00473                         }
00474 
00475                         triangulate(polygon, index, tList);
00476                         size= tList.size();
00477                         for (int i= 0; i < size; i+= 3)
00478                         {
00479                                 // Avoid normal problem
00480                                 if (faceIsCounterclockwise)
00481                                 {
00482                                         pIndexList->append(indexMap.value(face[tList[i]]));
00483                                         pIndexList->append(indexMap.value(face[tList[i + 1]]));
00484                                         pIndexList->append(indexMap.value(face[tList[i + 2]]));
00485                                 }
00486                                 else
00487                                 {
00488                                         pIndexList->append(indexMap.value(face[tList[i + 2]]));
00489                                         pIndexList->append(indexMap.value(face[tList[i + 1]]));
00490                                         pIndexList->append(indexMap.value(face[tList[i]]));
00491                                 }
00492                         }
00493                         Q_ASSERT(size == pIndexList->size());
00494         }
00495 
00496 }
00497 
00498 bool glc::lineIntersectPlane(const GLC_Line3d& line, const GLC_Plane& plane, GLC_Point3d* pPoint)
00499 {
00500         const GLC_Vector3d n= plane.normal();
00501         const GLC_Point3d p= line.startingPoint();
00502         const GLC_Vector3d d= line.direction();
00503 
00504         const double denominator= d * n;
00505         if (qFuzzyCompare(fabs(denominator), 0.0))
00506         {
00507                 qDebug() << " glc::lineIntersectPlane : Line parallel to the plane";
00508                 // The line is parallel to the plane
00509                 return false;
00510         }
00511         else
00512         {
00513                 // The line intersect the plane by one point
00514                 const double t= -((n * p) + plane.coefD()) / denominator;
00515                 (*pPoint)= p + (t * d);
00516 
00517                 return true;
00518         }
00519 }
00520 
00521 GLC_Point3d glc::project(const GLC_Point3d& point, const GLC_Line3d& line)
00522 {
00523         // Create the plane from the point with normal define by the line direction
00524         const GLC_Plane plane(line.direction().normalize(), point);
00525         GLC_Point3d intersection;
00526         const bool intersect= lineIntersectPlane(line, plane, &intersection);
00527         Q_ASSERT(intersect == true);
00528         return intersection;
00529 }
00530 
00531 

SourceForge.net Logo

©2005-2011 Laurent Ribon