//---------------------------------------------------------------------------- // EgalTech 2014-2014 //---------------------------------------------------------------------------- // File : Triangulate.cpp Data : 08.04.14 Versione : 1.5d2 // Contenuto : Implementazione della classe Triangulate. // // // // Modifiche : 08.04.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "Triangulate.h" #include "\EgtDev\Include\EGkPolyLine.h" #include "\EgtDev\Include\EGkPlane3d.h" //---------------------------------------------------------------------------- // In : PolyLine // Out : PNTVECTOR (Point3d Vector) : points of the polyline // INTVECTOR (int Vector) : 3*T indices of above points for T triangles //---------------------------------------------------------------------------- bool Triangulate::Make( const PolyLine& PL, PNTVECTOR& vPt, INTVECTOR& vTr) { // verifico che la polilinea sia chiusa -> poligono if ( ! PL.IsClosed()) return false ; // calcolo il piano medio del poligono Plane3d plPlane ; if ( ! PL.IsPlanar( plPlane, 50 * EPS_SMALL)) return false ; int nPlane ; bool bCCW ; if ( fabs( plPlane.vtN.z) >= fabs( plPlane.vtN.x) && fabs( plPlane.vtN.z) >= fabs( plPlane.vtN.y)) { nPlane = PL_XY ; bCCW = ( plPlane.vtN.z > 0) ; } else if ( fabs( plPlane.vtN.x) >= fabs( plPlane.vtN.y)) { nPlane = PL_YZ ; bCCW = ( plPlane.vtN.x > 0) ; } else { nPlane = PL_ZX ; bCCW = ( plPlane.vtN.y > 0) ; } // riempio il vettore con i vertici del poligono da triangolare vPt.clear() ; vPt.reserve( PL.GetPointNbr() - 1) ; // se orientato correttamente (componente di N > 0) if ( bCCW) { // salto il primo punto (coincide con l'ultimo) Point3d ptP ; if ( ! PL.GetFirstPoint( ptP)) return false ; // inserisco i punti while ( PL.GetNextPoint( ptP)) vPt.push_back( ptP) ; } // altrimenti lo prendo al contrario else { // salto l'ultimo punto (coincide con il primo) Point3d ptP ; if ( ! PL.GetLastPoint( ptP)) return false ; // inserisco i punti while ( PL.GetPrevPoint( ptP)) vPt.push_back( ptP) ; } // eseguo la triangolazione return MakeByEC( vPt, nPlane, vTr) ; } //---------------------------------------------------------------------------- // Triangulate the CCW n-gon specified by the vertices vPt (Pt[n] != Pt[0]) // Ear Clipping algorithm //---------------------------------------------------------------------------- bool Triangulate::MakeByEC( const PNTVECTOR& vPt, int nPlane, INTVECTOR& vTr) { // At least 3 points int n = int( vPt.size()) ; if ( n < 3) return false ; // Save the plane if ( nPlane != PL_XY && nPlane != PL_YZ && nPlane != PL_ZX) return false ; m_nPlane = nPlane ; // Clear and preallocate triangle vector ( #triangles = n - 2) vTr.clear() ; vTr.reserve( 3 * ( n - 2)) ; // Set up previous and next links to effectively form a double-linked vertex list INTVECTOR prev( n) ; INTVECTOR next( n) ; for ( int i = 0 ; i < n ; ++ i) { prev[i] = i - 1 ; next[i] = i + 1 ; } prev[0] = n - 1 ; next[n - 1] = 0 ; // Start at vertex 0 int i = 0 ; int nCount = n ; // Keep removing vertices until just a triangle left while ( n >= 3) { // To avoid infinite loop if ( nCount <= 0) return false ; -- nCount ; // Test if current vertex, v[i], is an ear bool bIsEar = true ; // An ear must be convex (here counterclockwise) if ( TriangleIsCCW( vPt[prev[i]], vPt[i], vPt[next[i]])) { // Loop over all vertices not part of the tentative ear int k = next[next[i]] ; do { // If vertex k is inside the ear triangle, then this is not an ear if ( TestPointInTriangle( vPt[k], vPt[prev[i]], vPt[i], vPt[next[i]])) { bIsEar = false ; break; } k = next[k] ; } while (k != prev[i]) ; } else { // The ‘ear’ triangle is clockwise so v[i] is not an ear bIsEar = false ; } // If current vertex v[i] is an ear, delete it and visit the previous vertex if ( bIsEar) { // Triangle (v[prev[i]], v[i], v[next[i]]) is an ear vTr.push_back( prev[i]) ; vTr.push_back( i) ; vTr.push_back( next[i]) ; // ‘Delete’ vertex v[i] by redirecting next and previous links // of neighboring verts past it. Decrement vertex count next[prev[i]] = next[i] ; prev[next[i]] = prev[i] ; n--; // Visit the previous vertex next i = prev[i] ; // Reset Count nCount = n ; } else { // Current vertex is not an ear; visit the next vertex i = next[i] ; } } return true ; } //---------------------------------------------------------------------------- bool Triangulate::TriangleIsCCW( const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) { double dV11 ; double dV12 ; double dV21 ; double dV22 ; switch ( m_nPlane) { default : // PL_XY dV11 = ptB.x - ptA.x ; dV12 = ptB.y - ptA.y ; dV21 = ptC.x - ptB.x ; dV22 = ptC.y - ptB.y ; break ; case PL_YZ : dV11 = ptB.y - ptA.y ; dV12 = ptB.z - ptA.z ; dV21 = ptC.y - ptB.y ; dV22 = ptC.z - ptB.z ; break ; case PL_ZX : dV11 = ptB.z - ptA.z ; dV12 = ptB.x - ptA.x ; dV21 = ptC.z - ptB.z ; dV22 = ptC.x - ptB.x ; break ; } return ( ( dV11 * dV22 - dV12 * dV21) > 0) ; } //---------------------------------------------------------------------------- // Test if point p is contained in triangle (a, b, c) bool Triangulate::TestPointInTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) { if ( ! TriangleIsCCW( ptA, ptB, ptP)) return false ; if ( ! TriangleIsCCW( ptB, ptC, ptP)) return false ; if ( ! TriangleIsCCW( ptC, ptA, ptP)) return false ; return true ; }