//---------------------------------------------------------------------------- // EgalTech 2013-2014 //---------------------------------------------------------------------------- // File : SurfTriMesh.cpp Data : 26.03.14 Versione : 1.5c9 // Contenuto : Implementazione della classe Superfici TriMesh. // // // // Modifiche : 26.03.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "SurfTriMesh.h" #include "GeoObjFactory.h" #include "NgeWriter.h" #include "NgeReader.h" #include "DistPointLine.h" #include "Triangulate.h" #include "\EgtDev\Include\EGkStringUtils3d.h" #include "\EgtDev\Include\EGkPolyLine.h" #include using namespace std ; //---------------------------------------------------------------------------- GEOOBJ_REGISTER( SRF_TRIMESH, NGE_S_TRM, SurfTriMesh) ; //---------------------------------------------------------------------------- SurfTriMesh::SurfTriMesh( void) : m_nStatus( TO_VERIFY), m_bClosed( false) { } //---------------------------------------------------------------------------- SurfTriMesh::~SurfTriMesh( void) { } //---------------------------------------------------------------------------- bool SurfTriMesh::Init( int nNumVert, int nNumTria) { // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // verifico validità parametri if ( nNumVert < 3 || nNumTria < 1) return false ; // prealloco la memoria try { m_vVert.reserve( nNumVert) ; m_vTria.reserve( nNumTria) ; } catch (...) { return false ; } // completo inizializzazione m_nStatus = OK ; m_bClosed = false ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::AddVertex( const Point3d& ptVert) { // imposto ricalcolo m_nStatus = TO_VERIFY ; m_OGrMgr.Reset() ; // inserisco il vertice try { m_vVert.push_back( StmVert( ptVert)) ;} catch(...) { return SVT_NULL ;} // ne determino l'indice return int( m_vVert.size() - 1) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetVertex( int nInd, const StmVert& vV) { // imposto ricalcolo m_nStatus = TO_VERIFY ; m_OGrMgr.Reset() ; // recupero la dimensione originale int nPrevSize = int( m_vVert.size()) ; // determino la dimensione necessaria int nNewSize = max( nInd + 1, nPrevSize) ; // se necessaria dimensione maggiore if ( nNewSize > nPrevSize) { // espando vettore try { m_vVert.resize( nNewSize) ; } catch (...) { return false ; } // inizializzo a cancellati gli eventuali vertici intermedi if ( ( nNewSize - nPrevSize) > 1) { StmVert vEmpty( Point3d(), SVT_DEL, 0) ; for ( int i = nPrevSize ; i < nNewSize ; ++ i) m_vVert[i] = vEmpty ; } } // inserisco il vertice m_vVert[nInd] = vV ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::AddTriangle( const int nIdVert[3]) { // verifico che i tre indici diano diversi if ( nIdVert[0] == nIdVert[1] || nIdVert[1] == nIdVert[2] || nIdVert[2] == nIdVert[0]) return SVT_DEL ; // imposto ricalcolo m_nStatus = TO_VERIFY ; m_OGrMgr.Reset() ; // inserisco il triangolo try { m_vTria.push_back( StmTria( nIdVert)) ;} catch(...) { return SVT_NULL ;} // ne determino l'indice return int( m_vTria.size() - 1) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetTriangle( int nInd, const StmTria& tT) { // imposto ricalcolo m_nStatus = TO_VERIFY ; m_OGrMgr.Reset() ; // recupero la dimensione originale int nPrevSize = int( m_vTria.size()) ; // determino la dimensione necessaria int nNewSize = max( nInd + 1, nPrevSize) ; // se necessaria dimensione maggiore if ( nNewSize > nPrevSize) { // espando vettore try { m_vTria.resize( nNewSize) ; } catch (...) { return false ; } // inizializzo a cancellati gli eventuali triangoli intermedi if ( ( nNewSize - nPrevSize) > 1) { StmTria tEmpty ; tEmpty.nIdVert[0] = SVT_DEL ; for ( int i = nPrevSize ; i < nNewSize ; ++ i) m_vTria[i] = tEmpty ; } } // inserisco il vertice m_vTria[nInd] = tT ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFirstVertex( Point3d& ptP) const { return GetNextVertex( SVT_NULL, ptP) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetNextVertex( int nId, Point3d& ptP) const { // cerco il primo successivo valido do { nId ++ ; } while ( nId < GetVertexNum() && m_vVert[nId].nIdTria == SVT_DEL) ; // se oltrepassata fine if ( nId >= GetVertexNum()) return SVT_NULL ; // recupero i dati ptP = m_vVert[nId].ptP ; // ritorno indice triangolo corrente return nId ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFirstTriangle( Triangle3d& Tria) const { return GetNextTriangle( SVT_NULL, Tria) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetNextTriangle( int nId, Triangle3d& Tria) const { // cerco il primo successivo valido do { nId ++ ; } while ( nId < GetTriangleNum() && m_vTria[nId].nIdVert[0] == SVT_DEL) ; // se oltrepassata fine if ( nId >= GetTriangleNum()) return SVT_NULL ; // recupero i dati Tria.Set( m_vVert[m_vTria[nId].nIdVert[0]].ptP, m_vVert[m_vTria[nId].nIdVert[1]].ptP, m_vVert[m_vTria[nId].nIdVert[2]].ptP, m_vTria[nId].vtN) ; // ritorno indice triangolo corrente return nId ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangleSmoothNormals( int nId, TriNormals3d& TNrms) const { // verifico esistenza del triangolo if ( nId >= GetTriangleNum() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; // recupero le normali di ciascun vertice for ( int i = 0 ; i < 3 ; ++ i) { if ( ! GetTriangleSmoothNormal( nId, i, TNrms.vtN[i])) TNrms.vtN[i] = m_vTria[nId].vtN ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangleSmoothNormal( int nT, int nV, Vector3d& vtN) const { // recupero tutti i triangoli attorno al vertice bool bCirc ; INTVECTOR vT ; int nTria = GetAllTriaAroundVertex( m_vTria[nT].nIdVert[nV], vT, bCirc) ; if ( nTria < 1) return false ; // cerco indice del triangolo corrente int nPos = - 1 ; for ( int i = 0 ; i < int( vT.size()) ; ++ i) { if ( vT[i] == nT) { nPos = i ; break ; } } if ( nPos == -1) return false ; // medio le normali, finché non incontro degli spigoli vtN = m_vTria[nT].vtN ; const double COS_DEV_LIM = cos( 22.5 * DEGTORAD) ; // parto dal triangolo e vado in direzione positiva int nLim = nPos ; for ( int i = NextIndAroundVertex( nPos, nTria, bCirc) ; i != nPos && i < int( vT.size()) ; i = NextIndAroundVertex( i, nTria, bCirc)) { if ( m_vTria[vT[nPos]].vtN * m_vTria[vT[i]].vtN >= COS_DEV_LIM) vtN += m_vTria[vT[i]].vtN ; else break ; nLim = i ; } // parto dal triangolo e vado in direzione negativa for ( int i = PrevIndAroundVertex( nPos, nTria, bCirc) ; i != nLim && i >= 0 ; i = PrevIndAroundVertex( i, nTria, bCirc)) { if ( m_vTria[vT[nPos]].vtN * m_vTria[vT[i]].vtN >= COS_DEV_LIM) vtN += m_vTria[vT[i]].vtN ; else break ; } //// parto dal triangolo e vado in direzione positiva //for ( int i = nPos + 1 ; i < int( vT.size()) ; ++ i) { // if ( m_vTria[vT[nPos]].vtN * m_vTria[vT[i]].vtN >= COS_DEV_LIM) // vtN += m_vTria[vT[i]].vtN ; // else // break ; //} //// parto dal triangolo e vado in direzione negativa //for ( int i = nPos - 1 ; i >= 0 ; -- i) { // if ( m_vTria[vT[nPos]].vtN * m_vTria[vT[i]].vtN >= COS_DEV_LIM) // vtN += m_vTria[vT[i]].vtN ; // else // break ; //} vtN.Normalize() ; return true ; } //---------------------------------------------------------------------------- SurfTriMesh* SurfTriMesh::Clone( void) const { // alloco oggetto SurfTriMesh* pStm = new(nothrow) SurfTriMesh ; if ( pStm != nullptr) { if ( ! pStm->Copy( *this)) { delete pStm ; return nullptr ; } } return pStm ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Copy( const IGeoObj* pGObjSrc) { const SurfTriMesh* pStm = dynamic_cast( pGObjSrc) ; if ( pStm == nullptr) return false ; return Copy( *pStm) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Copy( const SurfTriMesh& stmSrc) { if ( &stmSrc == this) return true ; if ( ! Init( stmSrc.GetVertexNum(), stmSrc.GetTriangleNum())) return false ; m_vVert = stmSrc.m_vVert ; m_vTria = stmSrc.m_vTria ; m_bClosed = stmSrc.m_bClosed ; m_nStatus = stmSrc.m_nStatus ; return true ; } //---------------------------------------------------------------------------- GeoObjType SurfTriMesh::GetType( void) const { return static_cast( GEOOBJ_GETTYPE( SurfTriMesh)) ; } //---------------------------------------------------------------------------- const string& SurfTriMesh::GetTitle( void) const { static const string sTitle = "TriMesh" ; return sTitle ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Dump( string& sOut, const char* szNewLine) const { // se superficie aperta o chiusa sOut += ( m_bClosed ? "Closed" : "Open") ; // numero di vertici sOut += " VertNbr=" + ToString( GetVertexNum()) ; // numero di triangoli sOut += " TriaNbr=" + ToString( GetTriangleNum()) ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetNgeId( void) const { return GEOOBJ_GETNGEID( SurfTriMesh) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Save( NgeWriter& ngeOut) const { // flag aperta/chiusa ngeOut.WriteBool( m_bClosed, ";") ; // numero di vertici ngeOut.WriteInt( GetVertexNum(), ";") ; // numero di triangoli ngeOut.WriteInt( GetTriangleNum(), ";", true) ; // ciclo sui vertici for ( int i = 0 ; i < int( m_vVert.size()) ; ++ i) { ngeOut.WriteInt( i, ";") ; ngeOut.WritePoint( m_vVert[i].ptP, ";") ; ngeOut.WriteInt( m_vVert[i].nIdTria, ";") ; ngeOut.WriteInt( m_vVert[i].nFlag, ";", true) ; } // ciclo sui triangoli for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { ngeOut.WriteInt( i, ";") ; ngeOut.WriteInt( m_vTria[i].nIdVert[0], ",") ; ngeOut.WriteInt( m_vTria[i].nIdVert[1], ",") ; ngeOut.WriteInt( m_vTria[i].nIdVert[2], ";") ; ngeOut.WriteInt( m_vTria[i].nIdAdjac[0], ",") ; ngeOut.WriteInt( m_vTria[i].nIdAdjac[1], ",") ; ngeOut.WriteInt( m_vTria[i].nIdAdjac[2], ";") ; ngeOut.WriteVector( m_vTria[i].vtN, ";") ; ngeOut.WriteInt( m_vTria[i].nTFlag, ";") ; ngeOut.WriteInt( m_vTria[i].nEFlag, ";", true) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Load( NgeReader& ngeIn) { // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // leggo la prossima linea ( 3 parametri : flag chiuso num vertici e num tria) // recupero il flag bool bClosed ; if ( ! ngeIn.ReadBool( bClosed, ";")) return false ; // recupero il numero di vertici int nNumVert ; if ( ! ngeIn.ReadInt( nNumVert, ";")) return false ; // recupero il numero di triangoli int nNumTria ; if ( ! ngeIn.ReadInt( nNumTria, ";", true)) return false ; // inizializzo la superficie TriMesh if ( ! Init( nNumVert, nNumTria)) return false ; m_bClosed = bClosed ; // lettura dei vertici int nInd ; StmVert vV ; for ( int i = 0 ; i < nNumVert ; ++ i) { // leggo la prossima linea ( 4 parametri : Indice, Punto, IdTria, Flag) // la interpreto e imposto il vertice if ( ! ngeIn.ReadInt( nInd, ";") || ! ngeIn.ReadPoint( vV.ptP, ";") || ! ngeIn.ReadInt( vV.nIdTria, ";") || ! ngeIn.ReadInt( vV.nFlag, ";", true) || ! SetVertex( nInd, vV)) return false ; } // lettura dei triangoli StmTria tT ; for ( int i = 0 ; i < nNumTria ; ++ i) { // leggo la prossima linea ( 6 parametri : Indice, IdVertici, IdAdiacenze, Normale, TFlag, EFlag) // la interpreto e imposto il vertice if ( ! ngeIn.ReadInt( nInd, ";") || ! ngeIn.ReadInt( tT.nIdVert[0], ",") || ! ngeIn.ReadInt( tT.nIdVert[1], ",") || ! ngeIn.ReadInt( tT.nIdVert[2], ";") || ! ngeIn.ReadInt( tT.nIdAdjac[0], ",") || ! ngeIn.ReadInt( tT.nIdAdjac[1], ",") || ! ngeIn.ReadInt( tT.nIdAdjac[2], ";") || ! ngeIn.ReadVector( tT.vtN, ";") || ! ngeIn.ReadInt( tT.nTFlag, ";") || ! ngeIn.ReadInt( tT.nEFlag, ";", true) || ! SetTriangle( nInd, tT)) return false ; } // eseguo validazione return Validate() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Validate( void) { if ( m_nStatus == OK) return true ; // Verifico che i vertici riferiti dai triangoli esistano m_nStatus = OK ; for ( int i = 0 ; i < GetTriangleNum() && m_nStatus == OK ; ++ i) { // se triangolo non cancellato if ( m_vTria[i].nIdVert[0] != SVT_DEL) { for ( int j = 0 ; j < 3 && m_nStatus == OK ; ++ j) { if ( m_vTria[i].nIdVert[j] < 0 || m_vTria[i].nIdVert[j] >= GetVertexNum() || m_vVert[ m_vTria[i].nIdVert[j]].nIdTria == SVT_DEL) m_nStatus = ERR ; } // calcolo eventuali normale mancante if ( m_vTria[i].vtN.IsSmall()) CalcTriangleNormal( i) ; } } // Verifico che i triangoli riferiti dai vertici esistano for ( int i = 0 ; i < GetVertexNum() && m_nStatus == OK ; ++ i) { // se vertice non cancellato e con riferimento assegnato if ( m_vVert[i].nIdTria != SVT_DEL && m_vVert[i].nIdTria != SVT_NULL) { if ( m_vVert[i].nIdTria < SVT_DEL || m_vVert[i].nIdTria >= GetTriangleNum() || m_vTria[ m_vVert[i].nIdTria].nIdVert[0] == SVT_DEL) m_nStatus = ERR ; } } return ( m_nStatus == OK) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::FindVertexInTria( int nV, int nT, int& nK) const { nK = - 1 ; for ( int k = 0 ; k < 3 ; k ++) { if ( nV == m_vTria[nT].nIdVert[k]) { nK = k ; break ; } } return ( nK != -1) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetAllTriaAroundVertex( int nV, INTVECTOR& vT, bool& bCirc) const { const int MAX_VT_SIZE = 512 ; // pulisco il vettore risultato vT.clear() ; // recupero il triangolo puntato dal vertice int nT = m_vVert[nV].nIdTria ; if ( nT == SVT_NULL) return 0 ; vT.push_back( nT) ; // cerco i triangoli adiacenti con lo stesso vertice in CCW int k ; int nTa = nT ; do { if ( FindVertexInTria( nV, nTa, k)) nTa = m_vTria[nTa].nIdAdjac[Prev(k)] ; else nTa = SVT_NULL ; // per evitare cicli infiniti dovuti a triangoli invertiti if ( vT.size() >= 2 && nTa == vT[vT.size()-2]) break ; // se valido if ( nTa != nT && nTa != SVT_NULL) vT.push_back( nTa) ; } while ( nTa != nT && nTa != SVT_NULL && vT.size() < MAX_VT_SIZE) ; // se sono ritornato al triangolo di partenza ho fatto un giro e concluso la ricerca if ( nTa == nT) { bCirc = true ; return int( vT.size()) ; } // altrimenti, devo cercare i triangoli adiacenti con lo stesso vertice in CW nTa = nT ; do { if ( FindVertexInTria( nV, nTa, k)) nTa = m_vTria[nTa].nIdAdjac[k] ; else nTa = SVT_NULL ; // per evitare cicli infiniti dovuti a triangoli invertiti if ( vT.size() >= 2 && nTa == vT[vT.size()-2]) break ; // se valido if ( nTa != nT && nTa != SVT_NULL) vT.insert( vT.begin(), nTa) ; } while ( nTa != nT && nTa != SVT_NULL && vT.size() < MAX_VT_SIZE) ; bCirc = ( nTa == nT) ; return int( vT.size()) ; } //---------------------------------------------------------------------------- int SurfTriMesh::NextIndAroundVertex( int nInd, int nSize, bool bCirc) const { nInd = nInd + 1 ; if ( bCirc && nInd >= nSize) nInd = nInd % nSize ; return nInd ; } //---------------------------------------------------------------------------- int SurfTriMesh::PrevIndAroundVertex( int nInd, int nSize, bool bCirc) const { nInd = nInd - 1 ; if ( bCirc && nInd < 0) nInd = ( nInd + nSize) % nSize ; return nInd ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustAdjacencies( void) { // sistemo i puntatori dai vertici ai triangoli for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { // se triangolo non cancellato if ( m_vTria[i].nIdVert[0] != SVT_DEL) { for ( int j = 0 ; j < 3 ; ++ j) { if ( m_vVert[ m_vTria[i].nIdVert[j]].nIdTria == SVT_NULL) m_vVert[ m_vTria[i].nIdVert[j]].nIdTria = i ; } } } // sistemo i puntatori tra triangoli bool bModif ; do { bModif = false ; for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { // se triangolo non cancellato if ( m_vTria[i].nIdVert[0] != SVT_DEL) { for ( int j = 0 ; j < 3 ; ++ j) { // se adiacenza non risolta if ( m_vTria[i].nIdAdjac[j] == SVT_NULL) { // vertici all'estremo dello half-edge di indice j int nVi = m_vTria[i].nIdVert[j] ; int nVf = m_vTria[i].nIdVert[Next(j)] ; // indice altro vertice in altro triangolo int k ; int nN ; bool bCirc ; INTVECTOR vT ; // triangoli con il vertice all'inizio dello half-edge nN = GetAllTriaAroundVertex( nVi, vT, bCirc) ; for ( int l = 0 ; l < nN ; ++ l) { int nTi = vT[l] ; if ( nTi != i) { // se altro vertice in comune, aggiusto le adiacenze dei due triangoli if ( FindVertexInTria( nVf, nTi, k)) { m_vTria[i].nIdAdjac[j] = nTi ; // se half-hedge con orientazione opposta (come dovrebbe essere) // il successivo di k deve coincidere con j if ( m_vTria[nTi].nIdVert[Next(k)] == m_vTria[i].nIdVert[j]) m_vTria[nTi].nIdAdjac[k] = i ; else m_vTria[nTi].nIdAdjac[Prev(k)] = i ; bModif = true ; continue ; } } } // triangoli con il vertice alla fine dello half-edge nN = GetAllTriaAroundVertex( nVf, vT, bCirc) ; for ( int l = 0 ; l < nN ; ++ l) { int nTf = vT[l] ; if ( nTf != i) { // se altro vertice in comune, aggiusto le adiacenze dei due triangoli if ( FindVertexInTria( nVi, nTf, k)) { m_vTria[i].nIdAdjac[j] = nTf ; // se half-hedge con orientazione opposta (come dovrebbe essere) // il precedente di k deve coincidere con il successivo j if ( m_vTria[nTf].nIdVert[Prev(k)] == m_vTria[i].nIdVert[Next(j)]) m_vTria[nTf].nIdAdjac[Prev(k)] = i ; else m_vTria[nTf].nIdAdjac[k] = i ; bModif = true ; continue ; } } } } } } } } while ( bModif) ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustOrientations( int nLev, bool& bSomeWrong) { // inizializzo parametro di ritorno bSomeWrong = false ; // ciclo sui triangoli for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { // flags int nNull = 0 ; int nWrong = 0 ; // ciclo sui lati del triangolo for ( int j = 0 ; j < 3 ; ++ j) { // se non c'è adiacenza if ( m_vTria[i].nIdAdjac[j] == SVT_NULL) ++ nNull ; // la verifico else { // indice altro triangolo int nT = m_vTria[i].nIdAdjac[j] ; // indice altro half-hedge int nE = 0 ; if ( m_vTria[nT].nIdAdjac[1] == i) nE = 1 ; else if ( m_vTria[nT].nIdAdjac[2] == i) nE = 2 ; // verifico corrispondenza vertici ( i due inizi devono essere diversi) if ( m_vTria[i].nIdVert[j] == m_vTria[nT].nIdVert[nE]) ++ nWrong ; } } // aggiorno stato if ( nWrong > 0) bSomeWrong = true ; // se almeno uno errato e gli altri nulli, inverto il triangolo if ( nWrong >= nLev && ( nWrong + nNull) == 3) InvertTriangle( i) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::TestClosure( void) { // ciclo sui triangoli bool bClosed = true ; for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { if ( m_vTria[i].nIdAdjac[0] == SVT_NULL || m_vTria[i].nIdAdjac[1] == SVT_NULL || m_vTria[i].nIdAdjac[2] == SVT_NULL) bClosed = false ; } // aggiorno la chiusura della superficie m_bClosed = bClosed ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustTopology( void) { // verifica indici if ( ! Validate()) return false ; // verifica adiacenze if ( ! AdjustAdjacencies()) return false ; // verifica continuità orientazione int nLev = 3 ; bool bSomeWrong ; do { if ( ! AdjustOrientations( nLev, bSomeWrong)) return false ; -- nLev ; } while ( bSomeWrong && nLev > 0) ; // verifica chiusura if ( ! TestClosure()) return false ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByTriangulation( const PolyLine& PL) { // eseguo la triangolazione PNTVECTOR vPnt ; INTVECTOR vTria ; Triangulate Tri ; if ( ! Tri.Make( PL, vPnt, vTria)) return false ; // inizializzo la superficie int nVert = int( vPnt.size()) ; int nTria = int( vTria.size()) / 3 ; if ( ! Init( nVert, nTria)) return false ; // inserisco i vertici nella superficie for ( int i = 0 ; i < int( vPnt.size()) ; ++ i) { if ( AddVertex( vPnt[i]) == SVT_NULL) return false ; } // recupero i triangoli e li inserisco nella superficie int vV[3] ; for ( int i = 0 ; i < nTria ; ++i) { vV[0] = vTria[3*i] ; vV[1] = vTria[3*i+1] ; vV[2] = vTria[3*i+2] ; if ( AddTriangle( vV) == SVT_NULL) return false ; } // sistemo la topologia return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByExtrusion( const PolyLine& PL, const Vector3d& vtExtr) { // verifico che il vettore di estrusione sia non nullo if ( vtExtr.IsSmall()) return false ; // verifico se la polilinea è chiusa bool bClosed = PL.IsClosed() ; // costruisco la mesh int nPointNbr = PL.GetPointNbr() ; if ( ! Init( 2 * nPointNbr, 2 * nPointNbr)) return false ; // inserisco il primo vertice della polilinea e il suo estruso int nP = 0 ; int nV = -1 ; Point3d ptP ; if ( ! PL.GetFirstPoint( ptP)) return false ; ++ nP ; if ( AddVertex( ptP) == SVT_NULL || AddVertex( ptP + vtExtr) == SVT_NULL) return false ; nV += 2 ; // ciclo sui punti della polilinea (per inserire vertice e suo estruso + 2 triangoli per ogni punto) int nIdV[3] ; while ( PL.GetNextPoint( ptP)) { // incremento numero punto ++ nP ; // se polilinea aperta o non è l'ultimo punto if ( ! bClosed || nP < nPointNbr) { // aggiungo due nuovi vertici if ( AddVertex( ptP) == SVT_NULL || AddVertex( ptP + vtExtr) == SVT_NULL) return false ; nV += 2 ; // aggiungo triangolo in basso a sinistra nIdV[0] = nV - 3 ; nIdV[1] = nV - 1 ; nIdV[2] = nV - 2 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // aggiungo triangolo in alto a destra nIdV[0] = nV - 2 ; nIdV[1] = nV - 1 ; nIdV[2] = nV ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // altrimenti ultimo punto di polilinea chiusa else { // non devo aggiungere i vertici, perchè coincidono con quelli iniziali // aggiungo triangolo in basso a sinistra nIdV[0] = nV - 1 ; nIdV[1] = 0 ; nIdV[2] = nV ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // aggiungo triangolo in alto a destra nIdV[0] = nV ; nIdV[1] = 0 ; nIdV[2] = 1 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } } // sistemo la topologia return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByRevolution( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx, double dAngRot, double dStepRot) { // verifico che l'asse di rotazione sia non nullo if ( vtAx.IsSmall()) return false ; // verifico che l'angolo di rotazione sia significativo e non superi un giro if ( fabs( dAngRot) < EPS_ANG_SMALL) return false ; if ( fabs( dAngRot) > ANG_FULL) dAngRot = _copysign( ANG_FULL, dAngRot) ; // verifico se rotazione completa bool bFullRev = ( fabs( fabs( dAngRot) - ANG_FULL) < EPS_ANG_SMALL) ; // aggiusto il valore dell'angolo di step const double MIN_STEP_ROT = 1 ; const double MAX_STEP_ROT = 90 ; if ( fabs( dStepRot) < MIN_STEP_ROT) dStepRot = _copysign( MIN_STEP_ROT, dAngRot) ; else if ( fabs( dStepRot) > MAX_STEP_ROT) dStepRot = _copysign( MAX_STEP_ROT, dAngRot) ; else dStepRot = _copysign( dStepRot, dAngRot) ; // calcolo il numero di step int nStep = int( dAngRot / dStepRot) ; nStep = max( nStep, 1) ; dStepRot = dAngRot / nStep ; if ( bFullRev) -- nStep ; // verifico che la polilinea non attraversi l'asse di rivoluzione o lo tocchi in punti interni if ( ! VeryfyPolylineForRevolution( PL, ptAx, vtAx)) { LOG_ERROR( GetEGkLogger(), "StmCreateByRevolution : polyline inside meets axis") return false ; } // verifico se la polilinea è chiusa bool bClosed = PL.IsClosed() ; // costruisco la mesh int nPointNbr = PL.GetPointNbr() ; if ( ! Init( ( 1 + nStep) * nPointNbr, ( 1 + nStep) * nPointNbr)) return false ; // inserisco il primo punto della polilinea e i suoi ruotati int nP = 0 ; int nV = -1 ; Point3d ptP ; // recupero il punto if ( ! PL.GetFirstPoint( ptP)) return false ; ++ nP ; // inserisco il primo vertice if ( AddVertex( ptP) == SVT_NULL) return false ; ++ nV ; // verifico se il punto giace sull'asse bool bPrevOnAx = DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ; int nVPrevOnAx = nV ; // se non è sull'asse, inserisco le copie ruotate if ( ! bPrevOnAx) { for ( int i = 1 ; i <= nStep ; ++i) { ptP.Rotate( ptAx, vtAx, dStepRot * DEGTORAD) ; if ( AddVertex( ptP) == SVT_NULL) return false ; ++ nV ; } } // ciclo sui punti della polilinea (per inserire vertice e suoi ruotati + 2 triangoli per ogni punto) int nIdV[4] ; while ( PL.GetNextPoint( ptP)) { // incremento numero punto ++ nP ; // se polilinea aperta o non è l'ultimo punto if ( ! bClosed || nP < nPointNbr) { // aggiungo il primo vertice if ( AddVertex( ptP) == SVT_NULL) return false ; ++ nV ; // verifico se il punto giace sull'asse bool bOnAx = DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ; // ciclo sugli step for ( int i = 1 ; i <= nStep ; ++i) { // se non è sull'asse, inserisco le copie ruotate if ( ! bOnAx) { ptP.Rotate( ptAx, vtAx, dStepRot * DEGTORAD) ; if ( AddVertex( ptP) == SVT_NULL) return false ; ++ nV ; } // per i controlli già fatti non è possibile avere contemp. prec e corr su asse // se il precedente è sull'asse, aggiungo un solo triangolo if ( bPrevOnAx) { nIdV[0] = nVPrevOnAx ; nIdV[1] = nV ; nIdV[2] = nV - 1 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // se il corrente è sull'asse, aggiungo un solo triangolo else if ( bOnAx) { nIdV[0] = nV - ( nStep + 2) + i ; nIdV[1] = nIdV[0] + 1 ; nIdV[2] = nV ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // altrimenti aggiungo due triangoli else { nIdV[0] = nV - ( nStep + 2) ; nIdV[1] = nIdV[0] + 1 ; nIdV[2] = nV ; nIdV[3] = nV - 1 ; if ( ! AddBiTriangle( nIdV)) return false ; } } // se rivoluzione completa, aggiungo i due triangoli di chiusura if ( bFullRev) { // per i controlli già fatti non è possibile avere contemp. prec e corr su asse // se il precedente è sull'asse, aggiungo un solo triangolo if ( bPrevOnAx) { nIdV[0] = nVPrevOnAx ; nIdV[1] = nV - nStep ; nIdV[2] = nV ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // se il corrente è sull'asse, aggiungo un solo triangolo else if ( bOnAx) { nIdV[0] = nV - 1 ; nIdV[1] = nV - ( nStep + 1) ; nIdV[2] = nV ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // altrimenti aggiungo due triangoli else { nIdV[0] = nV - ( nStep + 1) ; nIdV[1] = nV - ( 2 * nStep + 1) ; nIdV[2] = nV - nStep ; nIdV[3] = nV ; if ( ! AddBiTriangle( nIdV)) return false ; } } bPrevOnAx = false ; } // altrimenti ultimo punto di polilinea chiusa else { for ( int i = 1 ; i <= nStep ; ++i) { // non devo aggiungere i vertici, perchè coincidono con quelli iniziali // aggiungo triangolo in basso a sinistra nIdV[0] = nV - nStep + i - 1 ; nIdV[1] = nV - nStep + i ; nIdV[2] = i ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // aggiungo triangolo in alto a destra nIdV[0] = nV - nStep + i - 1 ; nIdV[1] = i ; nIdV[2] = i - 1 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // se rivoluzione completa, aggiungo i due triangoli di chiusura if ( bFullRev) { // aggiungo triangolo in basso a sinistra nIdV[0] = nV ; nIdV[1] = nV - nStep ; nIdV[2] = 0 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // aggiungo triangolo in alto a destra nIdV[0] = nV ; nIdV[1] = 0 ; nIdV[2] = nStep ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } } } // sistemo la topologia return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VeryfyPolylineForRevolution( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx) const { // calcolo un riferimento con origine ptAx e asseZ vtAx Frame3d frAx ; if ( ! frAx.Set( ptAx, vtAx)) return false ; // numero di segmenti della polilinea int nTotSeg = PL.GetLineNbr() ; // flag di chiusa bool bClosed = PL.IsClosed() ; // recupero il primo punto e lo porto nel riferimento Ax e quindi lo proietto su XY Point3d ptPp ; if ( ! PL.GetFirstPoint( ptPp)) return false ; ptPp.ToLoc( frAx) ; ptPp.z = 0 ; // calcolo la distanza tra i segmenti della polilinea portati nel piano XY del rif Ax e l'origine int nSeg = 0 ; Point3d ptPc ; while ( PL.GetNextPoint( ptPc)) { // punto corrente ptPc.ToLoc( frAx) ; ptPc.z = 0 ; ++ nSeg ; // verifico distanza if ( DistPointLine( ORIG, ptPp, ptPc).IsSmall()) { if ( bClosed || ( ! ( nSeg == 1 && AreSamePointNear( ORIG, ptPp)) && ! ( nSeg == nTotSeg && AreSamePointNear( ORIG, ptPc)))) return false ; } // salvo punto ptPp = ptPc ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AddBiTriangle( const int nIdVert[4]) { // 0 <- 3 // | | // 1 -> 2 int nIdV[3] ; // se la diagonale 0->2 è più corta della 1->3 if ( SqDist( m_vVert[nIdVert[0]].ptP, m_vVert[nIdVert[2]].ptP) <= SqDist( m_vVert[nIdVert[1]].ptP, m_vVert[nIdVert[3]].ptP)) { // triangolo 0->1->2 nIdV[0] = nIdVert[0] ; nIdV[1] = nIdVert[1] ; nIdV[2] = nIdVert[2] ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // triangolo 0->2->3 nIdV[0] = nIdVert[0] ; nIdV[1] = nIdVert[2] ; nIdV[2] = nIdVert[3] ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // altrimenti uso la 1->3 else { // triangolo 0->1->3 nIdV[0] = nIdVert[0] ; nIdV[1] = nIdVert[1] ; nIdV[2] = nIdVert[3] ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // triangolo 1->2->3 nIdV[0] = nIdVert[1] ; nIdV[1] = nIdVert[2] ; nIdV[2] = nIdVert[3] ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetLocalBBox( BBox3d& b3Loc) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno il box in locale b3Loc.Reset() ; for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) b3Loc.Add( m_vVert[i].ptP) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetBBox( const Frame3d& frRef, BBox3d& b3Ref) const { // verifico lo stato if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // assegno il box nel riferimento b3Ref.Reset() ; for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) { Point3d ptTemp = m_vVert[i].ptP ; ptTemp.ToGlob( frRef) ; b3Ref.Add( ptTemp) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Translate( const Vector3d& vtMove) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // traslo i vertici for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.Translate( vtMove) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dell'asse di rotazione if ( vtAx.IsSmall()) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // ruoto i vertici for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; } // ruoto le normali delle facce for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) m_vTria[i].vtN.Rotate( vtAx, dCosAng, dSinAng) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico non sia nulla if ( fabs( dCoeffX) < EPS_ZERO && fabs( dCoeffY) < EPS_ZERO && fabs( dCoeffZ) < EPS_ZERO) return false ; // determino se contiene anche un mirror (numero dispari di coefficienti negativi) bool bMirror = ( dCoeffX < 0) ; bMirror = ( bMirror ? ( dCoeffY > 0) : ( dCoeffY < 0)) ; bMirror = ( bMirror ? ( dCoeffZ > 0) : ( dCoeffZ < 0)) ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // scalo i vertici for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ; } // aggiorno le facce for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) { // se c'è mirror, devo invertire la faccia if ( bMirror && ! InvertTriangle( i)) return false ; // aggiorno la normale if ( ! CalcTriangleNormal( i)) return false ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::InvertTriangle( int nT) { // controllo validità triangolo if ( m_vTria[nT].nIdVert[0] == SVT_DEL) return false ; // scambio di due vertici swap( m_vTria[nT].nIdVert[1], m_vTria[nT].nIdVert[2]) ; // scambio delle conseguenti due adiacenze swap( m_vTria[nT].nIdAdjac[0], m_vTria[nT].nIdAdjac[2]) ; // inversione della normale m_vTria[nT].vtN *= - 1 ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CalcTriangleNormal( int nT) { // controllo validità triangolo if ( m_vTria[nT].nIdVert[0] == SVT_DEL) return false ; // controllo validità vertici riferiti dal triangolo if ( m_vTria[nT].nIdVert[0] >= GetVertexNum() || m_vVert[m_vTria[nT].nIdVert[0]].nIdTria == SVT_DEL || m_vTria[nT].nIdVert[1] >= GetVertexNum() || m_vVert[m_vTria[nT].nIdVert[1]].nIdTria == SVT_DEL || m_vTria[nT].nIdVert[2] >= GetVertexNum() || m_vVert[m_vTria[nT].nIdVert[2]].nIdTria == SVT_DEL) return false ; // calcolo vettori come due lati consecutivi del triangolo Vector3d vtV1 = m_vVert[m_vTria[nT].nIdVert[1]].ptP - m_vVert[m_vTria[nT].nIdVert[0]].ptP ; Vector3d vtV2 = m_vVert[m_vTria[nT].nIdVert[2]].ptP - m_vVert[m_vTria[nT].nIdVert[1]].ptP ; Vector3d vtN = vtV1 ^ vtV2 ; // normale da prodotto vettoriale if ( ! vtN.Normalize( EPS_ZERO)) return false ; m_vTria[nT].vtN = vtN ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del piano di specchiatura if ( vtNorm.IsSmall()) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // specchio i vertici for ( int i = 0 ; i < GetVertexNum() ; ++ i) m_vVert[i].ptP.Mirror( ptOn, vtNorm) ; // inverto le facce for ( int i = 0 ; i < GetTriangleNum() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) { // inverto la faccia if ( ! InvertTriangle( i)) return false ; // aggiorno la normale if ( ! CalcTriangleNormal( i)) return false ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::ToGlob( const Frame3d& frRef) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // trasformo i vertici for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.ToGlob( frRef) ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::ToLoc( const Frame3d& frRef) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // trasformo i vertici for ( int i = 0 ; i < GetVertexNum() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.ToLoc( frRef) ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Invert( void) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // inverto i triangoli for ( int i = 0 ; i < GetTriangleNum() ; ++ i) InvertTriangle( i) ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; }