//---------------------------------------------------------------------------- // EgalTech 2014-2015 //---------------------------------------------------------------------------- // File : SurfTriMesh.cpp Data : 15.05.14 Versione : 1.5e5 // 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 "GeoObjFactory.h" #include "NgeWriter.h" #include "NgeReader.h" #include "DistPointLine.h" #include "Triangulate.h" #include "GeoConst.h" #include "\EgtDev\Include\EGkStringUtils3d.h" #include "\EgtDev\Include\EGkPointGrid3d.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_dLinTol( STM_STD_LIN_TOL), m_dBoundaryAng( STM_STD_BOUNDARY_ANG), m_dSmoothAng( STM_STD_SMOOTH_ANG), m_bOriented( false), m_bClosed( false), m_bFaceted( false), m_nTimeStamp(), m_nTempProp() { m_dCosBndAng = cos( m_dBoundaryAng * DEGTORAD) ; m_dCosSmAng = cos( m_dSmoothAng * DEGTORAD) ; } //---------------------------------------------------------------------------- SurfTriMesh::~SurfTriMesh( void) { } //---------------------------------------------------------------------------- bool SurfTriMesh::Init( int nNumVert, int nNumTria, int nNumFacet) { // 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) ; if ( nNumFacet > 0) m_vFacet.reserve( nNumFacet) ; } 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.emplace_back( 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 siano diversi if ( nIdVert[0] == nIdVert[1] || nIdVert[1] == nIdVert[2] || nIdVert[2] == nIdVert[0]) return SVT_DEL ; // verifico che i tre vertici siano ben definiti if ( nIdVert[0] < 0 || nIdVert[0] >= int( m_vVert.size()) || m_vVert[nIdVert[0]].nIdTria == SVT_DEL || nIdVert[1] < 0 || nIdVert[1] >= int( m_vVert.size()) || m_vVert[nIdVert[1]].nIdTria == SVT_DEL || nIdVert[2] < 0 || nIdVert[2] >= int( m_vVert.size()) || m_vVert[nIdVert[2]].nIdTria == SVT_DEL) return SVT_DEL ; // verifico che la normale sia ben definita // calcolo vettori come due lati consecutivi del triangolo Vector3d vtV1 = m_vVert[nIdVert[1]].ptP - m_vVert[nIdVert[0]].ptP ; Vector3d vtV2 = m_vVert[nIdVert[2]].ptP - m_vVert[nIdVert[1]].ptP ; // normale da prodotto vettoriale Vector3d vtN = vtV1 ^ vtV2 ; if ( vtN.IsZero()) return SVT_DEL ; // imposto ricalcolo m_nStatus = TO_VERIFY ; m_OGrMgr.Reset() ; // inserisco il triangolo try { m_vTria.emplace_back( 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 triangolo m_vTria[nInd] = tT ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetArea( double& dArea) const { // controllo parametro di ritorno if ( &dArea == nullptr) return false ; // inizio con area nulla dArea = 0 ; // la superficie deve essere validata if ( m_nStatus != OK) return false ; // sommo l'area di tutti i triangoli Triangle3d Tria ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { dArea += Tria.GetArea() ; nId = GetNextTriangle( nId, Tria) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetVolume( double& dVolume) const { // controllo parametro di ritorno if ( &dVolume == nullptr) return false ; // inizio con volume nullo dVolume = 0 ; // la superficie deve essere validata if ( m_nStatus != OK) return false ; // la superficie deve essere chiusa if ( ! IsClosed()) return true ; // sommo il volume con segno di tutte le piramidi dall'origine ad ogni faccia Triangle3d Tria ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { Vector3d vtA = ( Tria.GetP( 1) - Tria.GetP( 0)) ^ ( Tria.GetP( 2) - Tria.GetP( 0)) ; dVolume += ( Tria.GetP( 0) - ORIG) * vtA ; nId = GetNextTriangle( nId, Tria) ; } dVolume /= 6 ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetCentroid( Point3d& ptCen) const { // controllo parametro di ritorno if ( &ptCen == nullptr) return false ; // inizio con centro nell'origine ptCen = ORIG ; // la superficie deve essere validata if ( m_nStatus != OK) return false ; // se la superficie è chiusa, calcolo il centroide del solido if ( IsClosed()) { // applico le formule di R. Nurnberg Imperial College London ad ogni faccia Triangle3d Tria ; double dVolume = 0 ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { Vector3d vtA = ( Tria.GetP( 1) - Tria.GetP( 0)) ^ ( Tria.GetP( 2) - Tria.GetP( 0)) ; dVolume += ( Tria.GetP( 0) - ORIG) * vtA ; ptCen.x += vtA.x * (( Tria.GetP( 0).x + Tria.GetP( 1).x) * ( Tria.GetP( 0).x + Tria.GetP( 1).x) + ( Tria.GetP( 1).x + Tria.GetP( 2).x) * ( Tria.GetP( 1).x + Tria.GetP( 2).x) + ( Tria.GetP( 2).x + Tria.GetP( 0).x) * ( Tria.GetP( 2).x + Tria.GetP( 0).x)) ; ptCen.y += vtA.y * (( Tria.GetP( 0).y + Tria.GetP( 1).y) * ( Tria.GetP( 0).y + Tria.GetP( 1).y) + ( Tria.GetP( 1).y + Tria.GetP( 2).y) * ( Tria.GetP( 1).y + Tria.GetP( 2).y) + ( Tria.GetP( 2).y + Tria.GetP( 0).y) * ( Tria.GetP( 2).y + Tria.GetP( 0).y)) ; ptCen.z += vtA.z * (( Tria.GetP( 0).z + Tria.GetP( 1).z) * ( Tria.GetP( 0).z + Tria.GetP( 1).z) + ( Tria.GetP( 1).z + Tria.GetP( 2).z) * ( Tria.GetP( 1).z + Tria.GetP( 2).z) + ( Tria.GetP( 2).z + Tria.GetP( 0).z) * ( Tria.GetP( 2).z + Tria.GetP( 0).z)) ; nId = GetNextTriangle( nId, Tria) ; } dVolume /= 6 ; if ( fabs( dVolume) < EPS_SMALL * EPS_SMALL * EPS_SMALL) return false ; ptCen /= ( 24 * 2 * dVolume) ; } // altrimenti calcolo il centroide della superficie else { Triangle3d Tria ; double dArea = 0 ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { double dTriaArea = Tria.GetArea() ; dArea += dTriaArea ; ptCen += Tria.GetCentroid() * dTriaArea ; nId = GetNextTriangle( nId, Tria) ; } if ( fabs( dArea) < EPS_SMALL * EPS_SMALL) return false ; ptCen /= dArea ; } return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetVertexNum( void) const { // calcolo il numero dei vertici cancellati int nErased = 0 ; for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) { if ( m_vVert[nId].nIdTria == SVT_DEL) ++ nErased ; } return ( GetVertexSize() - nErased) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetTriangleNum( void) const { // calcolo il numero dei triangoli cancellati int nErased = 0 ; for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { if ( m_vTria[nId].nIdVert[0] == SVT_DEL) ++ nErased ; } return ( GetTriangleSize() - nErased) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetVertex( int nId, Point3d& ptP) const { // verifico esistenza del vertice if ( nId < 0 || nId >= GetVertexSize() || m_vVert[nId].nIdTria == SVT_DEL) return false ; // recupero i dati ptP = m_vVert[nId].ptP ; 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 < GetVertexSize() && m_vVert[nId].nIdTria == SVT_DEL) ; // se oltrepassata fine if ( nId >= GetVertexSize()) return SVT_NULL ; // recupero i dati ptP = m_vVert[nId].ptP ; // ritorno indice triangolo corrente return nId ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangle( int nId, int nIdVert[3]) const { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; // recupero i dati nIdVert[0] = m_vTria[nId].nIdVert[0] ; nIdVert[1] = m_vTria[nId].nIdVert[1] ; nIdVert[2] = m_vTria[nId].nIdVert[2] ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFirstTriangle( int nIdVert[3]) const { return GetNextTriangle( SVT_NULL, nIdVert) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetNextTriangle( int nId, int nIdVert[3]) const { // cerco il primo successivo valido do { nId ++ ; } while ( nId < GetTriangleSize() && m_vTria[nId].nIdVert[0] == SVT_DEL) ; // se oltrepassata fine if ( nId >= GetTriangleSize()) return SVT_NULL ; // recupero i dati nIdVert[0] = m_vTria[nId].nIdVert[0] ; nIdVert[1] = m_vTria[nId].nIdVert[1] ; nIdVert[2] = m_vTria[nId].nIdVert[2] ; // ritorno indice triangolo corrente return nId ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangle( int nId, Triangle3d& Tria) const { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; // 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) ; return true ; } //---------------------------------------------------------------------------- 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 < GetTriangleSize() && m_vTria[nId].nIdVert[0] == SVT_DEL) ; // se oltrepassata fine if ( nId >= GetTriangleSize()) 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 ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetAllTriaAroundVertex( int nV, INTVECTOR& vT, bool& bCirc) const { const int MAX_VT_SIZE = 512 ; // pulisco il vettore risultato vT.clear() ; // verifico esistenza del vertice if ( nV < 0 || nV >= GetVertexSize() || m_vVert[nV].nIdTria == SVT_DEL) return false ; // 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()) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetVertexSmoothNormal( int nV, int nT, Vector3d& vtN) const { // verifico esistenza del vertice if ( nV < 0 || nV >= GetVertexSize() || m_vVert[nV].nIdTria == SVT_DEL) return false ; // verifico esistenza del triangolo if ( nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL) return false ; // recupero la normale del vertice for ( int i = 0 ; i < 3 ; ++ i) { if ( nV == m_vTria[nT].nIdVert[i]) { if ( ! GetTriangleSmoothNormal( nT, i, vtN)) vtN = m_vTria[nT].vtN ; return true ; } } // il vertice non appartiene al triangolo return false ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangleBoundaryEdges( int nId, TriFlags3d& TFlags) const { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; // verifico i tre lati for ( int i = 0 ; i < 3 ; ++ i) { // indice triangolo adiacente al lato int nT = m_vTria[nId].nIdAdjac[i] ; // se già definite le facce, verifico indice faccia if ( m_bFaceted) { TFlags.bFlag[i] = ( nT == SVT_NULL || m_vTria[nId].nIdFacet != m_vTria[nT].nIdFacet) ; } // altrimenti verifico con le normali else // se non c'è triangolo adiacente o se forma un angolo oltre il limite, il lato è un contorno TFlags.bFlag[i] = ( nT == SVT_NULL || m_vTria[nId].vtN * m_vTria[nT].vtN < m_dCosBndAng) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangleSmoothNormals( int nId, TriNormals3d& TNrms) const { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || 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 ; // 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 >= m_dCosSmAng) 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 >= m_dCosSmAng) 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->CopyFrom( *this)) { delete pStm ; return nullptr ; } } return pStm ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CopyFrom( const IGeoObj* pGObjSrc) { const SurfTriMesh* pStm = dynamic_cast( pGObjSrc) ; if ( pStm == nullptr) return false ; return CopyFrom( *pStm) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CopyFrom( const SurfTriMesh& stmSrc) { if ( &stmSrc == this) return true ; if ( ! Init( stmSrc.GetVertexSize(), stmSrc.GetTriangleSize())) return false ; m_vVert = stmSrc.m_vVert ; m_vTria = stmSrc.m_vTria ; m_bOriented = stmSrc.m_bOriented ; m_bClosed = stmSrc.m_bClosed ; m_dLinTol = stmSrc.m_dLinTol ; m_dSmoothAng = stmSrc.m_dSmoothAng ; m_dCosSmAng = stmSrc.m_dCosSmAng ; m_nStatus = stmSrc.m_nStatus ; m_nTimeStamp = stmSrc.m_nTimeStamp ; m_nTempProp = stmSrc.m_nTempProp ; m_bFaceted = stmSrc.m_bFaceted ; m_vFacet = stmSrc.m_vFacet ; 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 { // area double dArea ; GetArea( dArea) ; sOut += "Area=" + ToString( dArea,1) + szNewLine ; // altri dati per superficie chiusa if ( m_bClosed) { double dVolume ; GetVolume( dVolume) ; sOut += "Closed Volume=" + ToString( dVolume,1) + szNewLine ; } // se segnalo eventuale incongruenza di orientamento if ( ! m_bOriented) sOut += string( "Inconsistent Orientation") + szNewLine ; // numero di vertici sOut += "Vert : Nbr=" + ToString( GetVertexNum()) + " Size=" + ToString( GetVertexSize()) + szNewLine ; // numero di triangoli sOut += "Tria : Nbr=" + ToString( GetTriangleNum()) + " Size=" + ToString( GetTriangleSize()) + szNewLine ; // numero facce, se calcolate if ( m_bFaceted) sOut += "Facet : Nbr=" + ToString( GetFacetNum()) + " Size=" + ToString( GetFacetSize()) + szNewLine ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetNgeId( void) const { return GEOOBJ_GETNGEID( SurfTriMesh) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Save( NgeWriter& ngeOut) const { // tolleranza lineare di costruzione if ( ! ngeOut.WriteDouble( m_dLinTol, ";")) return false ; // angolo limite per mediare le normali if ( ! ngeOut.WriteDouble( m_dSmoothAng, ";", true)) return false ; // flag orientata if ( ! ngeOut.WriteBool( m_bOriented, ";")) return false ; // flag aperta/chiusa if ( ! ngeOut.WriteBool( m_bClosed, ";")) return false ; // numero di vertici if ( ! ngeOut.WriteInt( GetVertexSize(), ";")) return false ; // numero di triangoli if ( ! ngeOut.WriteInt( GetTriangleSize(), ";")) return false ; // numero di facce if ( ! ngeOut.WriteInt( GetFacetSize(), ";", true)) return false ; // ciclo sui vertici for ( int i = 0 ; i < int( m_vVert.size()) ; ++ i) { if ( ! ngeOut.WriteInt( i, ";") || ! ngeOut.WritePoint( m_vVert[i].ptP, ";") || ! ngeOut.WriteInt( m_vVert[i].nIdTria, ";") || ! ngeOut.WriteInt( m_vVert[i].nFlag, ";", true)) return false ; } // ciclo sui triangoli for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { if ( ! 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].nIdFacet, ";") || ! ngeOut.WriteInt( m_vTria[i].nTFlag, ";") || ! ngeOut.WriteInt( m_vTria[i].nEFlag, ";", true)) return false ; } // ciclo sulle facce int nNumFacet = int( m_vFacet.size()) ; for ( int i = 0 ; i < nNumFacet ; ++ i) { bool bEndL = ( ( i + 1) % 10 == 0) || i == ( nNumFacet - 1) ; if ( ! ngeOut.WriteInt( i, ",") || ! ngeOut.WriteInt( m_vFacet[i], ";", bEndL)) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Load( NgeReader& ngeIn) { // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // leggo la prossima linea ( 2 parametri : dLinTol e dSmoothAng) // tolleranza lineare di costruzione double dLinTol ; if ( ! ngeIn.ReadDouble( dLinTol, ";")) return false ; // angolo limite per mediare le normali double dSmoothAng ; if ( ! ngeIn.ReadDouble( dSmoothAng, ";", true)) return false ; m_dLinTol = dLinTol ; m_dSmoothAng = dSmoothAng ; m_dCosSmAng = cos( dSmoothAng * DEGTORAD) ; // leggo la prossima linea // prima di versione 1010 3 parametri : flag di chiusa, num vertici, num tria // da versione 1010 leggo flag di orientata bool bOriented ; bool bClosed ; int nNumVert ; int nNumTria ; int nNumFacet ; if ( ngeIn.GetFileVersion() < NGE_VER_1010) { bOriented = false ; if ( ! ngeIn.ReadBool( bClosed, ";")) return false ; if ( ! ngeIn.ReadInt( nNumVert, ";")) return false ; if ( ! ngeIn.ReadInt( nNumTria, ";", true)) return false ; nNumFacet = 0 ; } // da versione 1010 5 parametri : flag di orientata, flag di chiusa, num vertici, num tria, num facet else { if ( ! ngeIn.ReadBool( bOriented, ";")) return false ; if ( ! ngeIn.ReadBool( bClosed, ";")) return false ; if ( ! ngeIn.ReadInt( nNumVert, ";")) return false ; if ( ! ngeIn.ReadInt( nNumTria, ";")) return false ; if ( ! ngeIn.ReadInt( nNumFacet, ";", true)) return false ; } // preparo la superficie if ( ! Init( nNumVert, nNumTria, nNumFacet)) return false ; m_bOriented = bOriented ; m_bClosed = bClosed ; m_bFaceted = ( nNumFacet > 0) ; // 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 o 7 parametri : Indice, IdVertici, IdAdiacenze, Normale, [Facet,] 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.GetFileVersion() >= NGE_VER_1010 && ! ngeIn.ReadInt( tT.nIdFacet, ";")) || ! ngeIn.ReadInt( tT.nTFlag, ";") || ! ngeIn.ReadInt( tT.nEFlag, ";", true) || ! SetTriangle( nInd, tT)) return false ; } // lettura delle facce int nT ; for ( int i = 0 ; i < nNumFacet ; ++ i) { bool bEndL = ( ( i + 1) % 10 == 0) || i == ( nNumFacet - 1) ; if ( ! ngeIn.ReadInt( nInd, ",") || ! ngeIn.ReadInt( nT, ";", bEndL) || ! SetFacet( nInd, nT)) 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 < GetTriangleSize() && 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] >= GetVertexSize() || m_vVert[ m_vTria[i].nIdVert[j]].nIdTria == SVT_DEL) m_nStatus = ERR ; } // calcolo eventuale normale mancante if ( m_nStatus == OK && m_vTria[i].vtN.IsSmall()) CalcTriangleNormal( i) ; } } // Verifico che i triangoli riferiti dai vertici esistano for ( int i = 0 ; i < GetVertexSize() && 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 >= GetTriangleSize() || m_vTria[ m_vVert[i].nIdTria].nIdVert[0] == SVT_DEL) m_nStatus = ERR ; } } // Verifico che i triangoli riferiti dalle facce esistano for ( int i = 0 ; i < GetFacetSize() && m_nStatus == OK && m_bFaceted ; ++ i) { // verifico validità triangolo riferito if ( m_vFacet[i] <= SVT_NULL || m_vFacet[i] >= GetTriangleSize() || m_vTria[ m_vFacet[i]].nIdVert[0] == SVT_DEL) m_bFaceted = false ; } 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::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 < GetTriangleSize() ; ++ 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 < GetTriangleSize() ; ++ 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( void) { // se non ci sono almeno 2 triangoli è inutile fare test if ( m_vTria.size() < 2) { m_bOriented = true ; return true ; } // incremento time stamp di due step (serve un marcatore intermedio) m_nTimeStamp += 2 ; // ciclo sui triangoli TRINTDEQUE S3iQ ; bool bOk = true ; for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { // se triangolo non cancellato e senza time stamp if ( m_vTria[i].nIdVert[0] != SVT_DEL && abs( m_vTria[i].nTemp) != m_nTimeStamp) { S3iQ.emplace_back( i, SVT_NULL, 0) ; while ( ! S3iQ.empty()) { if ( ! AdjustTriaOrientation( S3iQ)) bOk = false ; } } } // salvo il risultato m_bOriented = bOk ; // in caso di errore ripristino l'orientamento originale if ( ! m_bOriented){ for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { if ( - m_vTria[i].nTemp == m_nTimeStamp) InvertTriangle( i) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustTriaOrientation( TRINTDEQUE& S3iQ) { // recupero e tolgo i dati dalla testa della coda int nT = S3iQ.front().nI1 ; int nRefT = S3iQ.front().nI2 ; int nRefE = S3iQ.front().nI3 ; S3iQ.pop_front() ; // assegno time stamp al triangolo m_vTria[nT].nTemp = m_nTimeStamp ; // se c'è triangolo di riferimento, devo verificare se da invertire if ( nRefT != SVT_NULL) { // cerco indice half-edge in comune int nE = 0 ; if ( m_vTria[nT].nIdAdjac[1] == nRefT) nE = 1 ; else if ( m_vTria[nT].nIdAdjac[2] == nRefT) nE = 2 ; // verifico corrispondenza vertici ( i due inizi devono essere diversi) if ( m_vTria[nT].nIdVert[nE] == m_vTria[nRefT].nIdVert[nRefE]) { m_vTria[nT].nTemp = - m_vTria[nT].nTemp ; InvertTriangle( nT) ; } } // verifico i triangoli adiacenti bool bOk = true ; for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; // se non c'è adiacenza o va sul triangolo di provenienza if ( nAdjT == SVT_NULL || nAdjT == nRefT) ; // la verifico else { // se triangolo adiacente senza timestamp finale e intermedio if ( abs( m_vTria[nAdjT].nTemp) != m_nTimeStamp && m_vTria[nAdjT].nTemp != ( m_nTimeStamp - 1)) { m_vTria[nAdjT].nTemp = m_nTimeStamp - 1 ; S3iQ.emplace_back( nAdjT, nT, j) ; } // altrimenti verifico che l'orientamento sia congruente else { // indice altro half-hedge int nE = 0 ; if ( m_vTria[nAdjT].nIdAdjac[1] == nT) nE = 1 ; else if ( m_vTria[nAdjT].nIdAdjac[2] == nT) nE = 2 ; // verifico corrispondenza vertici ( i due inizi devono essere diversi) if ( m_vTria[nT].nIdVert[j] == m_vTria[nAdjT].nIdVert[nE]) bOk = false ; } } } return bOk ; } //---------------------------------------------------------------------------- bool SurfTriMesh::TestSealing( void) { // ciclo sui triangoli bool bClosed = true ; for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { // se triangolo non cancellato if ( m_vTria[i].nIdVert[0] != SVT_DEL) { // verifico le adiacenze 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) { // dichiaro sfaccettatura da ricalcolare ResetFaceting() ; // verifica indici if ( ! Validate()) return false ; // verifica adiacenze if ( ! AdjustAdjacencies()) return false ; // verifica continuità orientazione if ( ! AdjustOrientations()) return false ; // verifica chiusura if ( ! TestSealing()) return false ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByFlatContour( const PolyLine& PL) { // eseguo la triangolazione, dopo aver verificato che il contorno sia chiuso e piatto 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::CreateByRegion( const POLYLINEVECTOR& vPL) { // eseguo la triangolazione, dopo aver verificato che l'insieme di contorni costituisca una regione PNTVECTOR vPnt ; INTVECTOR vTria ; Triangulate Tri ; if ( ! Tri.Make( vPL, 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 ; } // compatto e sistemo la topologia return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByExtrusion( const PolyLine& PL, const Vector3d& vtExtr) { // il vettore di estrusione deve essere non nullo if ( vtExtr.IsSmall()) return false ; // la polilinea deve avere almeno 2 punti if ( PL.GetPointNbr() < 2) return false ; // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; // 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 nV = -1 ; Point3d ptP ; if ( ! PL.GetFirstPoint( ptP)) return false ; 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[4] ; while ( PL.GetNextPoint( ptP, bClosed)) { // aggiungo due nuovi vertici if ( AddVertex( ptP) == SVT_NULL || AddVertex( ptP + vtExtr) == SVT_NULL) return false ; nV += 2 ; // aggiungo i due triangoli relativi nIdV[0] = nV - 2 ; nIdV[1] = nV - 3 ; nIdV[2] = nV - 1 ; nIdV[3] = nV ; if ( ! AddBiTriangle( nIdV)) return false ; } // se curva chiusa, aggiungo gli ultimi due triangoli if ( bClosed) { // non devo aggiungere i vertici, perchè coincidono con quelli iniziali // aggiungo i due triangoli relativi nIdV[0] = nV ; nIdV[1] = nV - 1 ; nIdV[2] = 0 ; nIdV[3] = 1 ; if ( ! AddBiTriangle( nIdV)) return false ; } // sistemo la topologia m_nStatus = TO_VERIFY ; return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2) { // verifico validità polilinee (devono avere almeno 2 punti e non coincidere se non agli estremi aperti) if ( ! VerifyPolylinesForTwoCurves( PL1, PL2)) return false ; // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; // flag di curve chiuse bool bClosed = PL1.IsClosed() && PL2.IsClosed() ; // recupero i parametri delle due polilinee double dU1F ; PL1.GetFirstU( dU1F) ; double dU1L ; PL1.GetLastU( dU1L) ; double dDeltaU1 = dU1L - dU1F ; if ( dDeltaU1 < EPS_SMALL) return false ; double dU2F ; PL2.GetFirstU( dU2F) ; double dU2L ; PL2.GetLastU( dU2L) ; double dDeltaU2 = dU2L - dU2F ; if ( dDeltaU2 < EPS_SMALL) return false ; // costruisco la mesh int nPointNbr = PL1.GetPointNbr() + PL2.GetPointNbr() ; if ( ! Init( nPointNbr, nPointNbr)) return false ; // recupero i punti iniziali su curva 1 int nV1p ; double dU1p ; double dA1p ; Point3d ptP1p ; int nV1s ; double dU1s ; double dA1s ; Point3d ptP1s ; bool bNext1 = PL1.GetFirstUPoint( &dU1p, &ptP1p) && PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ; if ( ! bNext1) return false ; dA1p = 0 ; dA1s = ( dU1s - dU1F) / dDeltaU1 ; if ( ( nV1p = AddVertex( ptP1p)) == SVT_NULL) return false ; // recupero i punti iniziali su curva 2 int nV2p ; double dU2p ; double dA2p ; Point3d ptP2p ; int nV2s ; double dU2s ; double dA2s ; Point3d ptP2s ; bool bNext2 = PL2.GetFirstUPoint( &dU2p, &ptP2p) && PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ; if ( ! bNext2) return false ; dA2p = 0 ; dA2s = ( dU2s - dU2F) / dDeltaU2 ; int nIdV[3] ; // se i punti iniziali non coincidono, inserisco il vertice iniziale di 2 if ( ! AreSamePointApprox( ptP1p, ptP2p)) { if ( ( nV2p = AddVertex( ptP2p)) == SVT_NULL) return false ; } // altrimenti, inserisco un triangolo e mi sposto in avanti su entrambe le curve else { // inserisco il vertice A1s if ( ( nV1s = AddVertex( ptP1s)) == SVT_NULL) return false ; // inserisco il vertice A2s if ( ( nV2s = AddVertex( ptP2s)) == SVT_NULL) return false ; // inserisco il triangolo A1p -> A1s -> A2s nIdV[0] = nV1p ; nIdV[1] = nV1s ; nIdV[2] = nV2s ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // passo al punto successivo su 1 nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ; bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ; if ( bNext1) dA1s = ( dU1s - dU1F) / dDeltaU1 ; // passo al punto successivo su 2 nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ; bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ; if ( bNext2) dA2s = ( dU2s - dU2F) / dDeltaU2 ; } // ciclo sui punti while ( bNext1 || bNext2) { // se c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s oppure non c'è dA2s if ( ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) || ! bNext2) { // inserisco il vertice A1s if ( ( nV1s = AddVertex( ptP1s)) == SVT_NULL) return false ; // inserisco il triangolo A2p -> A1p -> A1s nIdV[0] = nV2p ; nIdV[1] = nV1p ; nIdV[2] = nV1s ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // se i punti correnti coincidono passo al successivo anche su 2 if ( AreSamePointApprox( ptP1s, ptP2s)) { nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ; bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ; if ( bNext2) dA2s = ( dU2s - dU2F) / dDeltaU2 ; } // passo al punto successivo su 1 nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ; bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ; if ( bNext1) dA1s = ( dU1s - dU1F) / dDeltaU1 ; } // altrimenti è dA1p -> dA2s else { // inserisco il vertice A2s if ( ( nV2s = AddVertex( ptP2s)) == SVT_NULL) return false ; // inserisco il triangolo A2p -> A1p -> A2s nIdV[0] = nV2p ; nIdV[1] = nV1p ; nIdV[2] = nV2s ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // se i punti correnti coincidono passo al successivo anche su 1 if ( AreSamePointApprox( ptP1s, ptP2s)) { nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ; bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ; if ( bNext1) dA1s = ( dU1s - dU1F) / dDeltaU1 ; } // passo al punto successivo su 2 nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ; bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ; if ( bNext2) dA2s = ( dU2s - dU2F) / dDeltaU2 ; } } // se curve chiuse, aggiungo gli ultimi due triangoli if ( bClosed) { dA1s = 1 ; dA2s = 1 ; // se la diagonale più corta è dA2p -> dA1s = 0 if ( ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) { // inserisco il triangolo A2p -> A1p -> A1s = 0 nIdV[0] = nV2p ; nIdV[1] = nV1p ; nIdV[2] = 0 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // inserisco il triangolo A2p -> A1s = 0 -> A2s = 1 nIdV[0] = nV2p ; nIdV[1] = 0 ; nIdV[2] = 1 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // altrimenti è dA1p -> dA2s = 1 else { // inserisco il triangolo A2p -> A1p -> A2s = 1 nIdV[0] = nV2p ; nIdV[1] = nV1p ; nIdV[2] = 1 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // inserisco il triangolo A1p -> A1s = 0 -> A2s = 1 nIdV[0] = nV1p ; nIdV[1] = 0 ; nIdV[2] = 1 ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; } } // sistemo la topologia m_nStatus = TO_VERIFY ; return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyPolylinesForTwoCurves( const PolyLine& PL1, const PolyLine& PL2) const { // recupero se chiusa bool bClosed = PL1.IsClosed() && PL2.IsClosed() ; // se chiuse, devono avere almeno 3 punti if ( bClosed) { if ( PL1.GetPointNbr() < 3 || PL2.GetPointNbr() < 3) return false ; } // se aperte, almeno 2 else { if ( PL1.GetPointNbr() < 2 || PL2.GetPointNbr() < 2) return false ; } // verifico che non ci siano punti interni in comune a pari parametro // recupero i parametri delle due polilinee double dU1F ; PL1.GetFirstU( dU1F) ; double dU1L ; PL1.GetLastU( dU1L) ; double dDeltaU1 = dU1L - dU1F ; if ( dDeltaU1 < EPS_SMALL) return false ; double dU2F ; PL2.GetFirstU( dU2F) ; double dU2L ; PL2.GetLastU( dU2L) ; double dDeltaU2 = dU2L - dU2F ; if ( dDeltaU2 < EPS_SMALL) return false ; // ciclo sui punti double dA1p ; double dU1p ; Point3d ptP1p ; double dA1s ; double dU1s ; Point3d ptP1s ; bool bNext1 = PL1.GetFirstUPoint( &dU1p, &ptP1p) && PL1.GetNextUPoint( &dU1s, &ptP1s, true) ; if ( ! bNext1) return true ; dA1p = 0 ; dA1s = ( dU1s - dU1F) / dDeltaU1 ; double dA2p ; double dU2s ; Point3d ptP2s ; double dA2s ; double dU2p ; Point3d ptP2p ; bool bNext2 = PL2.GetFirstUPoint( &dU2p, &ptP2p) && PL2.GetNextUPoint( &dU2s, &ptP2s, true) ; if ( ! bNext2) return true ; dA2p = 0 ; dA2s = ( dU2s - dU2F) / dDeltaU2 ; // se chiuse, verifico la coincidenza dell'inizio delle due curve if ( bClosed && AreSamePointApprox( ptP1p, ptP2p)) return false ; // verifiche sui punti successivi (non sugli ultimi) while ( bNext1 || bNext2) { // se c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s oppure non c'è dA2s if ( ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) || ! bNext2) { // verifico se coincidono if ( AreSamePointApprox( ptP2p, ptP1s)) return false ; // passo al punto successivo su 1 dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ; bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, true) ; if ( bNext1) dA1s = ( dU1s - dU1F) / dDeltaU1 ; } // altrimenti è dA1p -> dA2s = 1 else { // verifico se coincidono if ( AreSamePointApprox( ptP1p, ptP2s)) return false ; // passo al punto successivo su 2 dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ; bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, true) ; if ( bNext2) dA2s = ( dU2s - dU2F) / dDeltaU2 ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByRevolution( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx, double dAngRot, double dStepRot) { return CreateByScrewing( PL, ptAx, vtAx, dAngRot, dStepRot, 0) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx, double dAngRot, double dStepRot, double dMove) { // verifico che l'asse di rotazione sia non nullo if ( vtAx.IsSmall()) return false ; // verifico se solo rivoluzione bool bOnlyRev = ( fabs( dMove) < EPS_SMALL) ; // verifico che l'angolo di rotazione sia significativo e, se solo rivoluzione, non superi un giro if ( fabs( dAngRot) < EPS_ANG_SMALL) return false ; if ( bOnlyRev && fabs( dAngRot) > ANG_FULL) dAngRot = _copysign( ANG_FULL, dAngRot) ; // verifico se rotazione completa bool bFullRev = bOnlyRev && ( 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) ; double dCosStepRot = cos( dAngRot / nStep * DEGTORAD) ; double dSinStepRot = sin( dAngRot / nStep * DEGTORAD) ; Vector3d vtStepMove = vtAx ; vtStepMove.Normalize() ; vtStepMove *= ( dMove / 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 ; } // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; // 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 nV = -1 ; Point3d ptP ; // recupero il punto if ( ! PL.GetFirstPoint( ptP)) return false ; // inserisco il primo vertice if ( AddVertex( ptP) == SVT_NULL) return false ; ++ nV ; // verifico se il punto giace sull'asse e vi sta fisso bool bPrevOnAx = bOnlyRev && DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ; int nVPrevOnAx = nV ; // se non è fisso sull'asse, inserisco le copie ruotate if ( ! bPrevOnAx) { for ( int i = 1 ; i <= nStep ; ++i) { ptP.Rotate( ptAx, vtAx, dCosStepRot, dSinStepRot) ; if ( ! bOnlyRev) ptP.Translate( vtStepMove) ; 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, bClosed)) { // aggiungo il primo vertice if ( AddVertex( ptP) == SVT_NULL) return false ; ++ nV ; // verifico se il punto giace sull'asse e vi sta fisso bool bOnAx = bOnlyRev && DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ; // ciclo sugli step for ( int i = 1 ; i <= nStep ; ++i) { // se non è fisso sull'asse, inserisco le copie ruotate if ( ! bOnAx) { ptP.Rotate( ptAx, vtAx, dCosStepRot, dSinStepRot) ; if ( ! bOnlyRev) ptP.Translate( vtStepMove) ; 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 if ( bClosed) { 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 m_nStatus = TO_VERIFY ; 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 && AreSamePointApprox( ORIG, ptPp)) && ! ( nSeg == nTotSeg && AreSamePointApprox( 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 è uguale o 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) + EPS_SMALL) { // 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::DoCompacting( void) { // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; // definisco un Grid per i vertici della superficie PointGrid3d VertGrid ; int nBuckets = GetVertexSize() ; VertGrid.Init( nBuckets) ; // inserisco i vertici della trimesh (evitando ripetizioni coi precedenti), // salvando in un vettore di reindirizzo i nuovi Id INTVECTOR vVId ; vVId.reserve( GetVertexSize()) ; for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) { // salto i vertici cancellati (ma ne occupo il posto nel vettore di reindirizzo) if ( m_vVert[nId].nIdTria == SVT_DEL) { vVId.push_back( SVT_DEL) ; continue ; } // recupero la posizione geometrica del vertice Point3d ptP = m_vVert[nId].ptP ; // se non c'è già un vertice con la stessa posizione lo inserisco nel grid int nAliasId ; if ( ! VertGrid.Find( ptP, 2 * EPS_SMALL, nAliasId)) { VertGrid.InsertPoint( ptP, nId) ; // salvo l'Id nel vettore di reindirizzo vVId.push_back( nId) ; } // c'e un vertice coincidente else { // salvo l'Id alias nel vettore di reindirizzo vVId.push_back( nAliasId) ; // marco il vertice come cancellato m_vVert[nId].nIdTria = SVT_DEL ; } } int nVIdSize = int( vVId.size()) ; // sistemo gli indici dei vertici nei triangoli for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { // recupero gli indici dei vertici del triangolo int vOId[3] ; vOId[0] = m_vTria[nId].nIdVert[0] ; vOId[1] = m_vTria[nId].nIdVert[1] ; vOId[2] = m_vTria[nId].nIdVert[2] ; // salto i triangoli cancellati if ( vOId[0] == SVT_DEL) continue ; // verifico la validità degli indici if ( vOId[0] < 0 || vOId[0] >= nVIdSize || vOId[1] < 0 || vOId[1] >= nVIdSize || vOId[2] < 0 || vOId[2] >= nVIdSize) return false ; // aggiorno il triangolo m_vTria[nId].nIdVert[0] = vVId[vOId[0]] ; m_vTria[nId].nIdVert[1] = vVId[vOId[1]] ; m_vTria[nId].nIdVert[2] = vVId[vOId[2]] ; } // ricalcolo le adiacenze m_nStatus = TO_VERIFY ; return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::DoSewing( const ISurfTriMesh& stmOther, const Frame3d& frOther) { // recupero l'altra superficie const SurfTriMesh* pOther = dynamic_cast( &stmOther) ; if ( pOther == nullptr) return false ; // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; // definisco un Grid per i vertici delle due superfici PointGrid3d VertGrid ; int nBuckets = GetVertexSize() + pOther->GetVertexSize() ; VertGrid.Init( nBuckets) ; // inserisco i vertici della trimesh corrente (li considero tutti diversi tra loro) for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) { // salto i vertici cancellati if ( m_vVert[nId].nIdTria == SVT_DEL) continue ; // inserisco il vertice nella griglia VertGrid.InsertPoint( m_vVert[nId].ptP, nId) ; } // inserisco i vertici dell'altra trimesh (evitando ripetizioni coi precedenti), // salvando in un vettore di reindirizzo i nuovi Id INTVECTOR vVId ; vVId.reserve( pOther->GetVertexSize()) ; for ( int nOId = 0 ; nOId < pOther->GetVertexSize() ; ++ nOId) { // salto i vertici cancellati (ma ne occupo il posto nel vettore di reindirizzo) if ( pOther->m_vVert[nOId].nIdTria == SVT_DEL) { vVId.push_back( SVT_DEL) ; continue ; } // recupero la posizione geometrica del vertice Point3d ptOP = pOther->m_vVert[nOId].ptP ; // la porto nel riferimento della prima superficie ptOP.ToGlob( frOther) ; // se non c'è già un vertice con la stessa posizione lo inserisco int nNewId ; if ( ! VertGrid.Find( ptOP, 2 * EPS_SMALL, nNewId)) { if ( ( nNewId = AddVertex( ptOP)) == SVT_NULL) return false ; VertGrid.InsertPoint( ptOP, nNewId) ; } // salvo il nuovo Id nel vettore di reindirizzo vVId.push_back( nNewId) ; } int nVIdSize = int( vVId.size()) ; // inserisco i triangoli dell'altra trimesh for ( int nOtId = 0 ; nOtId < pOther->GetTriangleSize() ; ++ nOtId) { // recupero gli indici dei vertici del triangolo int vOId[3] ; vOId[0] = pOther->m_vTria[nOtId].nIdVert[0] ; vOId[1] = pOther->m_vTria[nOtId].nIdVert[1] ; vOId[2] = pOther->m_vTria[nOtId].nIdVert[2] ; // salto i triangoli cancellati if ( vOId[0] == SVT_DEL) continue ; // verifico la validità degli indici if ( vOId[0] < 0 || vOId[0] >= nVIdSize || vOId[1] < 0 || vOId[1] >= nVIdSize || vOId[2] < 0 || vOId[2] >= nVIdSize) return false ; int vId[3] ; vId[0] = vVId[vOId[0]] ; vId[1] = vVId[vOId[1]] ; vId[2] = vVId[vOId[2]] ; // inserisco il triangolo if ( AddTriangle( vId) == SVT_NULL) return false ; } // ricalcolo le adiacenze m_nStatus = TO_VERIFY ; return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetLocalBBox( BBox3d& b3Loc, int nFlag) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno il box in locale b3Loc.Reset() ; for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) b3Loc.Add( m_vVert[i].ptP) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) 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 < GetVertexSize() ; ++ 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 < GetVertexSize() ; ++ 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 < GetVertexSize() ; ++ 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 < GetTriangleSize() ; ++ 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 < GetVertexSize() ; ++ 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 < GetTriangleSize() ; ++ 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.Invert() ; 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] < 0 || m_vTria[nT].nIdVert[0] >= GetVertexSize() || m_vVert[m_vTria[nT].nIdVert[0]].nIdTria == SVT_DEL || m_vTria[nT].nIdVert[1] < 0 || m_vTria[nT].nIdVert[1] >= GetVertexSize() || m_vVert[m_vTria[nT].nIdVert[1]].nIdTria == SVT_DEL || m_vTria[nT].nIdVert[2] < 0 || m_vTria[nT].nIdVert[2] >= GetVertexSize() || 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 < GetVertexSize() ; ++ i) m_vVert[i].ptP.Mirror( ptOn, vtNorm) ; // inverto le facce for ( int i = 0 ; i < GetTriangleSize() ; ++ 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::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dei parametri if ( vtNorm.IsSmall() || vtDir.IsSmall()) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // eseguo scorrimento dei vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.Shear( ptOn, vtNorm, vtDir, dCoeff) ; } // aggiorno le facce for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) { // 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 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo i vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.ToGlob( frRef) ; } 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 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo i vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.ToLoc( frRef) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dei frame if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR) return false ; // se i due riferimenti coincidono, non devo fare alcunché if ( AreSameFrame( frOri, frDest)) return true ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo i vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.LocToLoc( frOri, frDest) ; } 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 < GetTriangleSize() ; ++ i) InvertTriangle( i) ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; }