//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : SurfTriMeshFaceting.cpp Data : 25.02.15 Versione : 1.6b // Contenuto : Implementazione della classe Superfici TriMesh. // // // // Modifiche : 26.03.14 DS Creazione modulo. // 15.05.14 DS Corr. errore CreateByTwoCurves che dava loop infinito. // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "SurfTriMesh.h" #include "GeoConst.h" #include "PolygonPlane.h" using namespace std ; //---------------------------------------------------------------------------- bool SurfTriMesh::ResetFaceting( void) { m_bFaceted = false ; m_vFacet.clear() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyFaceting( void) const { if ( m_bFaceted) return true ; return (const_cast(this))->UpdateFaceting() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::UpdateFaceting( void) { // reset faceting m_bFaceted = false ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) m_vTria[i].nIdFacet = SVT_NULL ; // ricostruisco le sfaccettature int nFacet = 0 ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL && m_vTria[i].nIdFacet == SVT_NULL) { // assegno indice di faccia al triangolo m_vTria[i].nIdFacet = nFacet ; m_vFacet.push_back( i) ; ++ nFacet ; // piano del triangolo Plane3d plPlane ; if ( ! SetPlane( m_vVert[m_vTria[i].nIdVert[0]].ptP, m_vTria[i].vtN, plPlane)) return false ; // aggiorno i triangoli adiacenti for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[i].nIdAdjac[j] ; if ( nAdjT != SVT_NULL && m_vTria[nAdjT].nIdFacet == SVT_NULL) { if ( ! UpdateTriaFaceting( i, m_vTria[i].nIdFacet, plPlane, nAdjT)) return false ; } } } } // calcolo facce piane riuscito m_bFaceted = true ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::UpdateTriaFaceting( int nRefT, int nFacet, const Plane3d& plPlane, int nT) { // verifica scostamento della normale da quella del piano if ( plPlane.vtN * m_vTria[nT].vtN < m_dCosBndAng) return true ; // verifica scostamento del vertice opposto al lato in comune dal piano int nV = SVT_NULL ; for ( int i = 0 ; i < 3 ; ++i) { if ( m_vTria[nT].nIdAdjac[i] == nRefT) { nV = Prev( i) ; break ; } } if ( nV == SVT_NULL) return false ; if ( ! PointInPlaneApprox( m_vVert[m_vTria[nT].nIdVert[nV]].ptP, plPlane)) return true ; // il triangolo fa parte della faccia m_vTria[nT].nIdFacet = nFacet ; // aggiorno i triangoli adiacenti for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT != SVT_NULL && m_vTria[nAdjT].nIdFacet == SVT_NULL) { if ( ! UpdateTriaFaceting( nT, nFacet, plPlane, nAdjT)) return false ; } } return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFacetNum( void) const { // la superficie deve essere validata if ( m_nStatus != OK) return 0 ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return 0 ; // restituisco il numero return int( m_vFacet.size()) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetFacet( int nInd, int nT) { // recupero la dimensione originale int nPrevSize = int( m_vFacet.size()) ; // determino la dimensione necessaria int nNewSize = max( nInd + 1, nPrevSize) ; // se necessaria dimensione maggiore if ( nNewSize > nPrevSize) { // espando vettore try { m_vFacet.resize( nNewSize) ; } catch (...) { return false ; } // inizializzo a cancellate le eventuali facce intermedie if ( ( nNewSize - nPrevSize) > 1) { for ( int i = nPrevSize ; i < nNewSize ; ++ i) m_vFacet[i] = SVT_DEL ; } } // inserisco la faccia m_vFacet[nInd] = nT ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFacetFromTria( int nT) const { // la superficie deve essere validata if ( m_nStatus != OK) return SVT_NULL ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return SVT_NULL ; // l'indice del triangolo deve essere nei limiti if ( nT < 0 || nT >= int( m_vTria.size()) || m_vTria[nT].nIdVert[0] == SVT_DEL) return SVT_NULL ; // restituisco l'indice di faccia return m_vTria[nT].nIdFacet ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetAllTriaInFacet( int nF, INTVECTOR& vT) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return false ; // l'indice della faccia deve essere nei limiti if ( nF < 0 || nF >= int( m_vFacet.size())) return false ; // recupero l'indice del primo triangolo della faccia int nT = m_vFacet[nF] ; // incremento time stamp ++ m_nTimeStamp ; // recupero i triangoli della faccia vT.clear() ; vT.reserve( 10) ; vT.push_back( nT) ; m_vTria[nT].nTemp = m_nTimeStamp ; return VerifyAdjacTriaFacet( nT, vT) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyAdjacTriaFacet( int nT, INTVECTOR& vT) const { // verifico i triangoli adiacenti for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT != SVT_NULL && m_vTria[nAdjT].nTemp != m_nTimeStamp && m_vTria[nAdjT].nIdFacet == m_vTria[nT].nIdFacet) { vT.push_back( nAdjT) ; m_vTria[nAdjT].nTemp = m_nTimeStamp ; if ( ! VerifyAdjacTriaFacet( nAdjT, vT)) return false ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetNearestEndPoint( int nF, const Point3d& ptNear, Point3d& ptEnd, Vector3d& vtN) const { // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // ciclo sui triangoli e sui loro lati di bordo bool bFound = false ; double dMinSqDist = INFINITO * INFINITO ; for ( int i = 0 ; i < int( vTria.size()) ; ++i) { int nT = vTria[i] ; for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdFacet != nF) { Point3d ptTest = m_vVert[m_vTria[nT].nIdVert[j]].ptP ; double dSqDist = SqDist( ptTest, ptNear) ; if ( dSqDist < dMinSqDist) { // salvo i dati del punto dMinSqDist = dSqDist ; ptEnd = ptTest ; bFound = true ; // recupero la normale della faccia (non quella mediata del vertice) vtN = m_vTria[nT].vtN ; //Vector3d vtN1, vtN2 ; //if ( ! GetTriangleSmoothNormal( nT, j, vtN)) // vtN = m_vTria[nT].vtN ; } } } } return bFound ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetNearestMidPoint( int nF, const Point3d& ptNear, Point3d& ptMid, Vector3d& vtN) const { // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // ciclo sui triangoli e sui loro lati di bordo bool bFound = false ; double dMinSqDist = INFINITO * INFINITO ; for ( int i = 0 ; i < int( vTria.size()) ; ++i) { int nT = vTria[i] ; for ( int j = 0 ; j < 3 ; ++ j) { int k = Next( j) ; int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdFacet != nF) { Point3d ptTest = Media( m_vVert[m_vTria[nT].nIdVert[j]].ptP, m_vVert[m_vTria[nT].nIdVert[k]].ptP, 0.5) ; double dSqDist = SqDist( ptTest, ptNear) ; if ( dSqDist < dMinSqDist) { // salvo i dati del punto dMinSqDist = dSqDist ; ptMid = ptTest ; bFound = true ; // recupero la normale della faccia (non quella mediata del vertice) vtN = m_vTria[nT].vtN ; //Vector3d vtN1, vtN2 ; //if ( GetTriangleSmoothNormal( nT, j, vtN1) && // GetTriangleSmoothNormal( nT, k, vtN2)) { // vtN = Media( vtN1, vtN2, 0.5) ; // vtN.Normalize() ; //} //else // vtN = m_vTria[nT].vtN ; } } } } return bFound ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetLoops( int nF, POLYLINEVECTOR& vPL) const { // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // incremento time stamp ++ m_nTimeStamp ; // ciclo sui triangoli for ( int i = 0 ; i < int( vTria.size()) ; ++ i) { int nT = vTria[i] ; // se triangolo non ancora visitato if ( m_vTria[nT].nTemp != m_nTimeStamp) { // determino i triangoli adiacenti int nAdjT[3] ; for ( int j = 0 ; j < 3 ; ++ j) nAdjT[j] = m_vTria[nT].nIdAdjac[j] ; // se tutti e tre i lati sono di contorno if ( ( nAdjT[0] == SVT_NULL || m_vTria[nAdjT[0]].nIdFacet != nF) && ( nAdjT[1] == SVT_NULL || m_vTria[nAdjT[1]].nIdFacet != nF) && ( nAdjT[2] == SVT_NULL || m_vTria[nAdjT[2]].nIdFacet != nF)) { // ho trovato un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } // se i due lati 0 e 1 sono di contorno else if ( ( nAdjT[0] == SVT_NULL || m_vTria[nAdjT[0]].nIdFacet != nF) && ( nAdjT[1] == SVT_NULL || m_vTria[nAdjT[1]].nIdFacet != nF)) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nF, nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 1 e 2 sono di contorno else if ( ( nAdjT[1] == SVT_NULL || m_vTria[nAdjT[1]].nIdFacet != nF) && ( nAdjT[2] == SVT_NULL || m_vTria[nAdjT[2]].nIdFacet != nF)) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nF, nT, 0, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 2 e 0 sono di contorno else if ( ( nAdjT[2] == SVT_NULL || m_vTria[nAdjT[2]].nIdFacet != nF) && ( nAdjT[0] == SVT_NULL || m_vTria[nAdjT[0]].nIdFacet != nF)) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nF, nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 0 è di contorno else if ( nAdjT[0] == SVT_NULL || m_vTria[nAdjT[0]].nIdFacet != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nF, nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 1 è di contorno else if ( nAdjT[1] == SVT_NULL || m_vTria[nAdjT[1]].nIdFacet != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nF, nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se il lato 2 è di contorno else if ( nAdjT[2] == SVT_NULL || m_vTria[nAdjT[2]].nIdFacet != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nF, nT, 0, m_nTimeStamp, vPL.back())) return false ; } // altrimenti non c'è contorno else { // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } } } // normale di riferimento della faccia Vector3d vtN = m_vTria[vTria[0]].vtN ; // in prima posizione ci deve essere il loop esterno bool bOutFirst = false ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { Plane3d plPlane ; double dArea ; if ( ! vPL[i].IsClosedAndFlat( plPlane, dArea)) return false ; // se loop esterno if ( vtN * plPlane.vtN > 0) { // se non c'è ancora loop esterno in prima posizione if ( ! bOutFirst) { // lo sposto in prima posizione if ( i != 0) swap( vPL[0], vPL[i]) ; bOutFirst = true ; } // altrimenti errore else return false ; } } return bOutFirst ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MarchAlongLoop( int nF, int nT, int nV, int nTimeStamp, PolyLine& PL) const { // mi muovo lungo il loop, un triangolo alla volta bool bEnd = false ; while ( ! bEnd) { if ( ! MarchOneTria( nF, nT, nV, nTimeStamp, PL, bEnd)) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MarchOneTria( int nF, int& nT, int& nV, int nTimeStamp, PolyLine& PL, bool& bEnd) const { // verifico esistenza triangolo adiacente, sul lato dopo il vertice if ( m_vTria[nT].nIdAdjac[nV] == SVT_NULL) return false ; int nAdjT = m_vTria[nT].nIdAdjac[nV] ; // verifico appartenga alla stessa faccia if ( m_vTria[nAdjT].nIdFacet != nF) return false ; // recupero il suo lato di adiacenza int nAdjS = SVT_NULL ; for ( int i = 0 ; i < 3 ; ++ i) { if ( m_vTria[nAdjT].nIdAdjac[i] == nT) { nAdjS = i ; break ; } } if ( nAdjS == SVT_NULL) return false ; // vertice di fine adiacenza e indice del successivo lato int nAdjV = Next( nAdjS) ; // verifico se il lato successivo è un bordo int nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ; if ( nNextT == SVT_NULL || m_vTria[nNextT].nIdFacet != nF) { // se già recuperato if ( m_vTria[nAdjT].nTemp == nTimeStamp) { bEnd = true ; return true ; } // dichiaro triangolo analizzato m_vTria[nAdjT].nTemp = nTimeStamp ; // aggiungo il lato al loop nAdjV = Next( nAdjV) ; PL.AddUPoint( nAdjT, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ; // verifico anche il successivo nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ; if ( nNextT == SVT_NULL || m_vTria[nNextT].nIdFacet != nF) { // aggiungo il lato al loop nAdjV = Next( nAdjV) ; PL.AddUPoint( nAdjT, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ; } } // devo passare al triangolo adiacente nT = nAdjT ; nV = nAdjV ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetCenter( int nF, Point3d& ptCen, Vector3d& vtN) const { // recupero i loop della faccia POLYLINEVECTOR vPL ; if ( ! GetFacetLoops( nF, vPL) || vPL.size() == 0) return false ; // calcolo il centro del loop esterno (è il primo) PolygonPlane PolyPlane ; Point3d ptP ; for ( bool bFound = vPL[0].GetFirstPoint( ptP) ; bFound ; bFound = vPL[0].GetNextPoint( ptP)) PolyPlane.AddPoint( ptP) ; if ( ! PolyPlane.GetCentroid( ptCen)) return false ; // recupero la normale di un triangolo della faccetta vtN = m_vTria[m_vFacet[nF]].vtN ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetNormal( int nF, Vector3d& vtN) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return false ; // l'indice della faccia deve essere nei limiti if ( nF < 0 || nF >= int( m_vFacet.size())) return false ; // recupero la normale di un triangolo della faccetta vtN = m_vTria[m_vFacet[nF]].vtN ; return true ; }