//---------------------------------------------------------------------------- // EgalTech 2014-2022 //---------------------------------------------------------------------------- // File : SurfTriMesh.cpp Data : 12.08.22 Versione : 2.4h1 // Contenuto : Implementazione della classe Superfici TriMesh. // // // // Modifiche : 26.03.14 DS Creazione modulo. // 15.05.14 DS Corr. errore CreateByTwoCurves che dava loop infinito. // 02.01.19 LM Aggiunta gestione connessione. // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "SurfTriMesh.h" #include "GeoObjFactory.h" #include "GdbGeo.h" #include "NgeWriter.h" #include "NgeReader.h" #include "SurfFlatRegion.h" #include "Triangulate.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkDistLineLine.h" #include "/EgtDev/Include/EGkDistPointSurfTm.h" #include "/EgtDev/Include/EGkIntersLinePlane.h" #include "/EgtDev/Include/EGkPointGrid3d.h" #include "/EgtDev/Include/EGkPolygon3d.h" #include "/EgtDev/Include/EGkPolyLine.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EGkUiUnits.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include #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_bShowEdges( false), m_bOriented( false), m_bClosed( false), m_bFaceted( false), m_bFacEdged( false), m_nTimeStamp( 0), m_nTempProp{0,0}, m_dTempParam{0,0}, m_nMaxTFlag( 0), m_nShells( -1), m_pHGrd3d( nullptr) { m_dCosBndAng = cos( m_dBoundaryAng * DEGTORAD) ; m_dCosSmAng = cos( m_dSmoothAng * DEGTORAD) ; } //---------------------------------------------------------------------------- SurfTriMesh::~SurfTriMesh( void) { ResetHashGrids3d() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Init( int nNumVert, int nNumTria, int nNumFacet) { // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Clear() ; ResetHashGrids3d() ; // se superficie vuota if ( nNumVert == 0 && nNumTria == 0 && nNumFacet == 0) return true ; // 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 ; m_nMaxTFlag = 0 ; m_nShells = -1 ; m_vPart.clear() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Clear( void) { m_nStatus = OK ; m_dLinTol = STM_STD_LIN_TOL ; m_dBoundaryAng = STM_STD_BOUNDARY_ANG ; m_dCosBndAng = cos( m_dBoundaryAng * DEGTORAD) ; m_dSmoothAng = STM_STD_SMOOTH_ANG ; m_dCosSmAng = cos( m_dSmoothAng * DEGTORAD) ; m_bShowEdges = false ; m_bOriented = false ; m_bClosed = false ; m_bFaceted = false ; m_bFacEdged = false ; m_vVert.clear() ; m_vTria.clear() ; m_vFacet.clear() ; m_OGrMgr.Clear() ; ResetHashGrids3d() ; m_nTimeStamp = 0 ; m_nTempProp[0] = 0 ; m_nTempProp[1] = 0 ; m_dTempParam[0] = 0 ; m_dTempParam[1] = 0 ; m_nMaxTFlag = 0 ; m_nShells = -1 ; m_vPart.clear() ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::AddVertex( const Point3d& ptVert, double dU, double dV) { // imposto ricalcolo m_nStatus = TO_VERIFY ; m_nShells = -1 ; m_vPart.clear() ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // inserisco il vertice try { m_vVert.emplace_back( ptVert) ;} catch(...) { return SVT_NULL ;} // ne determino l'indice int nId = int( m_vVert.size() - 1) ; // aggiugo le coordinate corrispondenti allo spazio parametrico m_vVert[nId].dU = dU ; m_vVert[nId].dV = dV ; return nId ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MoveVertex( int nInd, const Point3d& ptNewVert) { // verifico validità indice if ( nInd < 0 || nInd >= int( m_vVert.size())) return false ; // verifico non sia già cancellato if ( m_vVert[nInd].nIdTria == SVT_DEL) return false ; // se non c'è spostamento, posso uscire if ( AreSamePointExact( m_vVert[nInd].ptP, ptNewVert)) return true ; // sposto il vertice m_vVert[nInd].ptP = ptNewVert ; // imposto ricalcolo m_nStatus = TO_VERIFY ; m_nShells = - 1 ; m_vPart.clear() ; m_bFaceted = false ; m_bFacEdged = false ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // per aggiornare completamente la superficie chiamare DoCompacting return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetVertex( int nInd, const StmVert& vV) { // imposto ricalcolo m_nStatus = TO_VERIFY ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // 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], int nTFlag) { // 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 ; double dSqN = vtN.SqLen() ; if ( dSqN < SQ_EPS_ZERO) return SVT_DEL ; // verifico la distanza minima tra i vertici e i lati opposti if ( dSqN < SQ_EPS_TRIA_H * max( { vtV1.SqLen(), vtV2.SqLen(), ( vtV1 + vtV2).SqLen()})) return SVT_DEL ; // imposto ricalcolo m_nStatus = TO_VERIFY ; m_nShells = -1 ; m_vPart.clear() ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; if ( ! vtN.Normalize( EPS_ZERO)) return SVT_DEL ; // inserisco il triangolo try { m_vTria.emplace_back( nIdVert, nTFlag) ;} catch(...) { return SVT_NULL ;} // aggiorno la sua normale m_vTria.back().vtN = vtN ; // aggiorno massimo TFlag m_nMaxTFlag = max( m_nMaxTFlag, nTFlag) ; // 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() ; ResetHashGrids3d() ; // 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 ; // aggiorno massimo TFlag m_nMaxTFlag = max( m_nMaxTFlag, tT.nTFlag) ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::RemoveTriangle( int nId) { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize()) return false ; // verifico se già cancellato if ( m_vTria[nId].nIdVert[0] == SVT_DEL) return true ; // aggiorno eventuali riferimenti dei vertici for ( int i = 0 ; i < 3 ; ++ i) { // indice vertice int nV = m_vTria[nId].nIdVert[i] ; // se vertice non c'è passo al prossimo if ( nV < 0 || nV >= int( m_vVert.size())) continue ; if ( m_vVert[nV].nIdTria == nId) { int nAdjP = m_vTria[nId].nIdAdjac[i] ; int nAdjM = m_vTria[nId].nIdAdjac[(i+2)%3] ; if ( nAdjP != SVT_NULL && m_vTria[nAdjP].nIdVert[0] != SVT_DEL) m_vVert[nV].nIdTria = nAdjP ; else if ( nAdjM != SVT_NULL && m_vTria[nAdjM].nIdVert[0] != SVT_DEL) m_vVert[nV].nIdTria = nAdjM ; else m_vVert[nV].nIdTria = SVT_NULL ; } } // annullo le adiacenze dei triangoli vicini a questo for ( int i = 0 ; i < 3 ; ++ i) { // indice triangolo adiacente int nAdjT = m_vTria[nId].nIdAdjac[i] ; // se triangolo adiacente non c'è passo al prossimo if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdVert[0] == SVT_DEL) continue ; // ne sistemo la contro-adiacenza for ( int j = 0 ; j < 3 ; ++ j) { if ( m_vTria[nAdjT].nIdAdjac[j] == nId) m_vTria[nAdjT].nIdAdjac[j] = SVT_NULL ; } } // dichiaro cancellato il triangolo m_vTria[nId].nIdVert[0] = SVT_DEL ; // invalido calcolo facce e loro bordi m_bFaceted = false ; m_bFacEdged = false ; // invalido calcolo connettività m_nShells = -1 ; m_vPart.clear() ; 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 ( abs( 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 ( abs( dArea) < SQ_EPS_SMALL) return false ; ptCen /= dArea ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::IsEmpty( void) const { // verifico presenza di almeno un vertice bool bVertFound = false ; for ( int nId = 0 ; nId < GetVertexSize() && ! bVertFound ; ++ nId) { if ( m_vVert[nId].nIdTria != SVT_DEL) bVertFound = true ; } if ( ! bVertFound) return true ; // verifico presenza di almeno un triangolo bool bTriaFound = false ; for ( int nId = 0 ; nId < GetTriangleSize() && ! bTriaFound ; ++ nId) { if ( m_vTria[nId].nIdVert[0] != SVT_DEL) bTriaFound = true ; } return ( ! bTriaFound) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetVertexCount( 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::GetTriangleCount( void) const { // calcolo il numero dei triangoli validi int nCount = 0 ; for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { if ( m_vTria[nId].nIdVert[0] != SVT_DEL) ++ nCount ; } return nCount ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetTriangleCount( int nTFlag) const { // calcolo il numero dei triangoli con flag indicato e non cancellati int nCount = 0 ; for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { if ( m_vTria[nId].nIdVert[0] != SVT_DEL && m_vTria[nId].nTFlag == nTFlag) ++ nCount ; } return nCount ; } //---------------------------------------------------------------------------- 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 ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetVertexParam( int nId, double& dU, double& dV) const { // verifico esistenza del vertice if ( nId < 0 || nId >= GetVertexSize() || m_vVert[nId].nIdTria == SVT_DEL) return false ; // recupero i dati (verso l'esterno sempre in 0..1..2..3..) dU = m_vVert[nId].dU / PREC_SCALE_COEFF ; dV = m_vVert[nId].dV / PREC_SCALE_COEFF ; 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 indice non valido if ( nId < 0 || 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 indice non valido if ( nId < 0 || 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) ; Tria.SetGrade( m_vTria[nId].nTFlag) ; 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 indice non valido if ( nId < 0 || 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) ; Tria.SetGrade( m_vTria[nId].nTFlag) ; // ritorno indice triangolo corrente return nId ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangle( int nId, Triangle3dEx& 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) ; Tria.SetGrade( m_vTria[nId].nTFlag) ; GetTriangleBoundaryEdges( nId, const_cast( Tria.GetTriFlags())) ; GetTriangleSmoothNormals( nId, const_cast( Tria.GetTriNormals())) ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFirstTriangle( Triangle3dEx& Tria) const { return GetNextTriangle( SVT_NULL, Tria) ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetNextTriangle( int nId, Triangle3dEx& Tria) const { // cerco il primo successivo valido do { nId ++ ; } while ( nId < GetTriangleSize() && m_vTria[nId].nIdVert[0] == SVT_DEL) ; // se indice non valido if ( nId < 0 || 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) ; Tria.SetGrade( m_vTria[nId].nTFlag) ; GetTriangleBoundaryEdges( nId, const_cast( Tria.GetTriFlags())) ; GetTriangleSmoothNormals( nId, const_cast( Tria.GetTriNormals())) ; // 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 0 ; // 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 { // ricerco triangolo if ( FindVertexInTria( nV, nTa, k)) nTa = m_vTria[nTa].nIdAdjac[Prev(k)] ; else nTa = SVT_NULL ; // se non valido, esco if ( nTa == nT || nTa == SVT_NULL) break ; // per evitare cicli infiniti dovuti a triangoli invertiti if ( find( vT.begin(), vT.end(), nTa) != vT.end()) break ; // inserisco in elenco vT.push_back( nTa) ; } while ( 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 { // ricerco triangolo if ( FindVertexInTria( nV, nTa, k)) nTa = m_vTria[nTa].nIdAdjac[k] ; else nTa = SVT_NULL ; // se non valido, esco if ( nTa == nT || nTa == SVT_NULL) break ; // per evitare cicli infiniti dovuti a triangoli invertiti if ( find( vT.begin(), vT.end(), nTa) != vT.end()) break ; // inserisco in elenco vT.insert( vT.begin(), nTa) ; } while ( 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::GetTriangleAdjacencies( int nId, int nIdAdjTriaId[3]) const { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; // recupero gli indici dei triangoli adiacenti for ( int i = 0 ; i < 3 ; ++ i) { nIdAdjTriaId[i] = m_vTria[nId].nIdAdjac[i] ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTFlag( int nTriaId, int& nTFlag) const { // verifico esistenza del triangolo if ( nTriaId < 0 || nTriaId >= GetTriangleSize() || m_vTria[nTriaId].nIdVert[0] == SVT_DEL) return false ; nTFlag = m_vTria[nTriaId].nTFlag ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTempInt( int nTriaId, int& nTempInt) const { // verifico esistenza del triangolo if ( nTriaId < 0 || nTriaId >= GetTriangleSize() || m_vTria[nTriaId].nIdVert[0] == SVT_DEL) return false ; nTempInt = m_vTria[nTriaId].nTemp ; 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 double dAngW = 1 ; GetTriangleVertexAngle( nT, nV, dAngW) ; vtN = dAngW * 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[nT].vtN * m_vTria[vT[i]].vtN >= m_dCosSmAng) { int nK ; double dAngW = 1 ; if ( FindVertexInTria( m_vTria[nT].nIdVert[nV], vT[i], nK) && GetTriangleVertexAngle( vT[i], nK, dAngW)) vtN += dAngW * 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[nT].vtN * m_vTria[vT[i]].vtN >= m_dCosSmAng) { int nK ; double dAngW = 1 ; if ( FindVertexInTria( m_vTria[nT].nIdVert[nV], vT[i], nK) && GetTriangleVertexAngle( vT[i], nK, dAngW)) vtN += dAngW * m_vTria[vT[i]].vtN ; } else break ; } vtN.Normalize() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangleVertexAngle( int nT, int nV, double& dAng) const { // verifico esistenza del triangolo e validità vertice if ( nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL || nV < 0 || nV > 2) return false ; // determino i vettori dei due lati che partono dal vertice Point3d ptVert = m_vVert[ m_vTria[nT].nIdVert[nV]].ptP ; Point3d ptPrev = m_vVert[ m_vTria[nT].nIdVert[Prev( nV)]].ptP ; Point3d ptNext = m_vVert[ m_vTria[nT].nIdVert[Next( nV)]].ptP ; Vector3d vtPrev = ptPrev - ptVert ; Vector3d vtNext = ptNext - ptVert ; // determino l'angolo tra i due lati return vtPrev.GetAngle( vtNext, dAng) ; } //---------------------------------------------------------------------------- SurfTriMesh* SurfTriMesh::CloneTriangle( int nT) const { // verifico validità superficie ed esistenza del triangolo if ( ! IsValid() || nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL) return nullptr ; // Creo nuovo oggetto SurfTriMesh PtrOwner pSurfTM( new( nothrow) SurfTriMesh) ; if ( IsNull( pSurfTM)) return nullptr ; // Copio il valore dei membri pSurfTM->m_dLinTol = m_dLinTol ; pSurfTM->m_dBoundaryAng = m_dBoundaryAng ; pSurfTM->m_dCosBndAng = m_dCosBndAng ; pSurfTM->m_dSmoothAng = m_dSmoothAng ; pSurfTM->m_dCosSmAng = m_dCosSmAng ; // Copio il triangolo int nNewInd[3] = { pSurfTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[0]].ptP), pSurfTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[1]].ptP), pSurfTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[2]].ptP)} ; if ( pSurfTM->AddTriangle( nNewInd, m_vTria[nT].nTFlag) == SVT_NULL) return nullptr ; // Aggiusto la superficie pSurfTM->DoCompacting() ; // Restituisco la nuova superficie return Release( pSurfTM) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetLoops( POLYLINEVECTOR& vPL) const { // verifico lo stato if ( m_nStatus != OK) return false ; // aggiorno timestamp dei triangoli ++ m_nTimeStamp ; for ( auto& Tria : m_vTria) Tria.nTemp = m_nTimeStamp ; // incremento time stamp ++ m_nTimeStamp ; // ciclo sui triangoli for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) { // se triangolo valido e non ancora visitato if ( m_vTria[nT].nIdVert[0] != SVT_DEL && m_vTria[nT].nTemp != m_nTimeStamp) { // determino i triangoli adiacenti int nAdjT[3] ; for ( int j = 0 ; j < 3 ; ++ j) nAdjT[j] = m_vTria[nT].nIdAdjac[j] ; // se tutti e tre i lati sono di contorno if ( nAdjT[0] == SVT_NULL && nAdjT[1] == SVT_NULL && nAdjT[2] == SVT_NULL) { // ho trovato un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } // se i due lati 0 e 1 sono di contorno else if ( nAdjT[0] == SVT_NULL && nAdjT[1] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 1 e 2 sono di contorno else if ( nAdjT[1] == SVT_NULL && nAdjT[2] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 2 e 0 sono di contorno else if ( nAdjT[2] == SVT_NULL && nAdjT[0] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 0 è di contorno else if ( nAdjT[0] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 1 è di contorno else if ( nAdjT[1] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se il lato 2 è di contorno else if ( nAdjT[2] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back())) return false ; } // altrimenti non c'è contorno else { // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MarchAlongLoop( int nT, int nV, int nTimeStamp, PolyLine& PL) const { // mi muovo lungo il loop, un triangolo alla volta int nCount = 0 ; bool bEnd = false ; while ( ! bEnd) { // altro triangolo if ( ! MarchOneTria( nT, nV, nTimeStamp, PL, bEnd)) return false ; // per evitare loop infiniti ++ nCount ; if ( nCount > 3 * int( m_vTria.size()) + 10) return true ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MarchOneTria( int& nT, int& nV, int nTimeStamp, PolyLine& PL, bool& bEnd) const { // verifico esistenza triangolo adiacente, sul lato dopo il vertice if ( m_vTria[nT].nIdAdjac[nV] == SVT_NULL) return false ; int nAdjT = m_vTria[nT].nIdAdjac[nV] ; // recupero il suo lato di adiacenza int nAdjS = SVT_NULL ; for ( int i = 0 ; i < 3 ; ++ i) { if ( m_vTria[nAdjT].nIdAdjac[i] == nT) { nAdjS = i ; break ; } } if ( nAdjS == SVT_NULL) return false ; // vertice di fine adiacenza e indice del successivo lato int nAdjV = Next( nAdjS) ; // verifico se il lato successivo è un bordo int nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ; if ( nNextT == SVT_NULL) { // se già recuperato if ( m_vTria[nAdjT].nTemp == nTimeStamp) { bEnd = true ; return true ; } // dichiaro triangolo analizzato m_vTria[nAdjT].nTemp = nTimeStamp ; // aggiungo il lato al loop nAdjV = Next( nAdjV) ; PL.AddUPoint( nAdjT, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ; // verifico anche il successivo nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ; if ( nNextT == SVT_NULL) { // aggiungo il lato al loop nAdjV = Next( nAdjV) ; PL.AddUPoint( nAdjT, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ; } } // devo passare al triangolo adiacente nT = nAdjT ; nV = nAdjV ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL, bool bAllTria) const { // Verifico lo stato if ( m_nStatus != OK) return false ; // Verifico la direzione Vector3d vtVers = vtDir ; if ( ! vtVers.Normalize()) return false ; // Controlli su tolleranza dTol = max( dTol, 100 * EPS_SMALL) ; // Determino il riferimento di proiezione Frame3d frOCS ; frOCS.Set( ORIG, vtVers) ; Frame3d frBox = frOCS ; frBox.Invert() ; BBox3d b3Box ; GetBBox( frBox, b3Box) ; frOCS.Translate( b3Box.GetMin().z * frOCS.VersZ()) ; // Ottengo la Silhouette come unione delle regioni dei triangoli proiettati // calcolo la regione dei triangoli proiettati PtrOwner pSfr ; Triangle3d Tria ; int nT = GetFirstTriangle( Tria) ; while ( nT != SVT_NULL) { // verifico la normale e il triangolo proiettato if ( ( ( bAllTria && abs( Tria.GetN() * vtVers) > EPS_ZERO) || ( ! bAllTria && Tria.GetN() * vtVers > EPS_ZERO)) && Tria.Scale( frOCS, 1, 1, 0) && Tria.GetSqMinHeight() > SQ_EPS_SMALL) { PtrOwner pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromTriangle( Tria))) ; if ( ! IsNull( pSfrTria)) { if ( bAllTria && Tria.GetN() * vtVers < 0) pSfrTria->Invert() ; pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ; if ( IsNull( pSfr)) pSfr.Set( pSfrTria) ; else pSfr->Add( *pSfrTria) ; } } // passo al successivo nT = GetNextTriangle( nT, Tria) ; } // Se non esiste la regione if ( IsNull( pSfr)) return false ; // Effettuo contro-offset pSfr->Offset( -dTol, ICurve::OFF_EXTEND) ; // Recupero i contorni della regione for ( int i = 0 ; i < pSfr->GetChunkCount() ; ++ i) { for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; ++ j) { PolyLine PL ; if ( pSfr->ApproxLoopWithLines( i, j, LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) vPL.emplace_back( PL) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL, bool bAllTria) const { // Verifico lo stato if ( m_nStatus != OK) return false ; // Verifico la direzione Vector3d vtVers = plPlane.GetVersN() ; if ( ! vtVers.Normalize()) return false ; // Controlli su tolleranza dTol = max( dTol, 100 * EPS_SMALL) ; // Determino il riferimento di proiezione Frame3d frOCS ; frOCS.Set( plPlane.GetPoint(), vtVers) ; // Ottengo la Silhouette come unione delle regioni dei triangoli proiettati (solo parti sopra il piano) // calcolo la regione dei triangoli proiettati PtrOwner pSfr ; Triangle3d Tria ; int nT = GetFirstTriangle( Tria) ; while ( nT != SVT_NULL) { // verifico la normale if ( ( bAllTria && abs( Tria.GetN() * vtVers) > EPS_ZERO) || ( ! bAllTria && Tria.GetN() * vtVers > EPS_ZERO)) { // ricavo il poligono equivalente al triangolo Polygon3d pgTria ; pgTria.FromTriangle( Tria) ; // taglio il poligono con il piano pgTria.Trim( plPlane, false, true, bAllTria) ; // se rimasto qualcosa if ( pgTria.GetSideCount() > 0) { // lo proietto sul piano e creo la regione if ( pgTria.Scale( frOCS, 1, 1, 0)) { PtrOwner pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLine( pgTria.GetPolyLine()))) ; if ( ! IsNull( pSfrTria)) { if ( bAllTria && Tria.GetN() * vtVers < 0) pSfrTria->Invert() ; pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ; if ( IsNull( pSfr)) pSfr.Set( pSfrTria) ; else pSfr->Add( *pSfrTria) ; } } } } // passo al successivo nT = GetNextTriangle( nT, Tria) ; } // Se non esiste la regione if ( IsNull( pSfr)) return true ; // Effettuo contro-offset pSfr->Offset( -dTol, ICurve::OFF_EXTEND) ; // Recupero i contorni della regione for ( int i = 0 ; i < pSfr->GetChunkCount() ; ++ i) { for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; ++ j) { PolyLine PL ; if ( pSfr->ApproxLoopWithLines( i, j, LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) vPL.emplace_back( PL) ; } } 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 = GetBasicSurfTriMesh( 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(), stmSrc.GetFacetSize())) return false ; m_nStatus = stmSrc.m_nStatus ; m_dLinTol = stmSrc.m_dLinTol ; m_dBoundaryAng = stmSrc.m_dBoundaryAng ; m_dCosBndAng = stmSrc.m_dCosBndAng ; m_dSmoothAng = stmSrc.m_dSmoothAng ; m_dCosSmAng = stmSrc.m_dCosSmAng ; m_bShowEdges = stmSrc.m_bShowEdges ; m_bOriented = stmSrc.m_bOriented ; m_bClosed = stmSrc.m_bClosed ; m_bFaceted = stmSrc.m_bFaceted ; m_bFacEdged = stmSrc.m_bFacEdged ; m_vVert = stmSrc.m_vVert ; m_vTria = stmSrc.m_vTria ; m_vFacet = stmSrc.m_vFacet ; m_vFacEdge = stmSrc.m_vFacEdge ; m_nTimeStamp = stmSrc.m_nTimeStamp ; m_nTempProp[0] = stmSrc.m_nTempProp[0] ; m_nTempProp[1] = stmSrc.m_nTempProp[1] ; m_nMaxTFlag = stmSrc.m_nMaxTFlag ; m_nShells = stmSrc.m_nShells ; m_vPart = stmSrc.m_vPart ; m_dTempParam[0] = stmSrc.m_dTempParam[0] ; m_dTempParam[1] = stmSrc.m_dTempParam[1] ; return true ; } //---------------------------------------------------------------------------- GeoObjType SurfTriMesh::GetType( void) const { return static_cast( GEOOBJ_GETTYPE( SurfTriMesh)) ; } //---------------------------------------------------------------------------- const string& SurfTriMesh::GetTitle( void) const { static const string sTitle = "SurfTm" ; return sTitle ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Dump( string& sOut, bool bMM, const char* szNewLine) const { // visualizzazione spigoli sOut += "ShowEdges=" + ToString( GetShowEdges()) + szNewLine ; // area double dArea ; GetArea( dArea) ; sOut += "Area=" + ToString( GetAreaInUiUnits( dArea, bMM),1) + szNewLine ; // altri dati per superficie chiusa if ( m_bClosed) { double dVolume ; GetVolume( dVolume) ; sOut += "Closed Volume=" + ToString( GetVolumeInUiUnits( dVolume, bMM), 1) + szNewLine ; } // segnalo eventuale incongruenza di orientamento if ( ! m_bOriented) sOut += string( "Inconsistent Orientation") + szNewLine ; // segnalo numero di parti e di gusci int nParts = GetPartCount() ; int nShells = GetShellCount() ; sOut += string( "Parts=") + ToString( nParts) + string( " Shells=") + ToString( nShells) + szNewLine ; // numero di vertici sOut += "Vert : Nbr=" + ToString( GetVertexCount()) + " Size=" + ToString( GetVertexSize()) + szNewLine ; // numero di triangoli sOut += "Tria : Nbr=" + ToString( GetTriangleCount()) + " Size=" + ToString( GetTriangleSize()) + szNewLine ; // numero facce, se calcolate if ( m_bFaceted) sOut += "Facet : Nbr=" + ToString( GetFacetCount()) + " 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::PreSave( GdbGeo& Wrapper) const { // salvo il flag di visualizzazione spigoli vivi anche in shading (default false) if ( m_bShowEdges) Wrapper.SetInfo( GDB_SI_SHOWEDGES, true) ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::PostSave( GdbGeo& Wrapper) const { // elimino eventuali info aggiunte nella PreSave Wrapper.RemoveInfo( GDB_SI_SHOWEDGES) ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Load( NgeReader& ngeIn) { // imposto ricalcolo della grafica, della connessione e di hashgrids3d m_OGrMgr.Clear() ; m_nMaxTFlag = 0 ; m_nShells = -1 ; m_vPart.clear() ; ResetHashGrids3d() ; // 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 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::PostLoad( GdbGeo& Wrapper) { // recupero eventuale flag di visualizzazione spigoli vivi anche in shading bool bVal ; if ( Wrapper.GetInfo( GDB_SI_SHOWEDGES, bVal)) { m_bShowEdges = bVal ; Wrapper.RemoveInfo( GDB_SI_SHOWEDGES) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Validate( bool bCorrect) { 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()) { if ( ! CalcTriangleNormal( i) && bCorrect) RemoveTriangle( 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) { if ( bCorrect) m_vVert[i].nIdTria = SVT_NULL ; else 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 ; } // invalido calcolo connessione m_nShells = -1 ; m_vPart.clear() ; return ( m_nStatus == OK) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::FindVertexInTria( int nV, int nT, int& nK) const { nK = - 1 ; for ( int k = 0 ; k < 3 ; k ++) { if ( nT != SVT_NULL && 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::AdjustVertices( 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 == SVT_DEL) m_vVert[ m_vTria[i].nIdVert[j]].nIdTria = i ; } } } // tutti i vertici che non puntano a triangoli vanno ora considerati cancellati for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria == SVT_NULL) m_vVert[i].nIdTria = SVT_DEL ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustAdjacencies( bool AdjustVert) { // sistemo le relazioni tra triangoli e vertici if ( AdjustVert) { if ( ! AdjustVertices()) return false ; } // matrice di incidenza vertici-triangoli INTMATRIX mVertTria( GetVertexSize()) ; 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) { int nVert = m_vTria[i].nIdVert[j] ; mVertTria[nVert].emplace_back( 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 ; // triangoli con il vertice all'inizio dello half-edge nN = int( mVertTria[nVi].size()) ; for ( int l = 0 ; l < nN ; ++ l) { int nTi = mVertTria[nVi][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 = int( mVertTria[nVf].size()) ; for ( int l = 0 ; l < nN ; ++ l) { int nTf = mVertTria[nVf][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 ; break ; } } } // aggiorno la chiusura della superficie m_bClosed = bClosed ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustTopology( void) { // se non è rimasto alcunché di valido, pulisco tutto ed esco if ( GetVertexCount() < 3 || GetTriangleCount() < 1) { Clear() ; m_bOriented = true ; m_bClosed = true ; return true ; } // dichiaro sfaccettatura e relativi bordi da ricalcolare m_bFaceted = false ; m_bFacEdged = false ; // invalido calcolo connessione m_nShells = - 1 ; m_vPart.clear() ; // verifica indici if ( ! Validate( true)) return false ; // verifica adiacenze if ( ! AdjustAdjacencies()) return false ; // verifica continuità orientazione if ( ! AdjustOrientations()) return false ; // verifica chiusura if ( ! TestSealing()) return false ; // verifica sfaccettatura if ( ! VerifyFaceting()) return false ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::PackVertices( void) { // compatto, sovrascrivendo eventuali vertici cancellati INTVECTOR vVId ; vVId.reserve( GetVertexSize()) ; int nFirstFree = SVT_NULL ; for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) { if ( m_vVert[nId].nIdTria != SVT_DEL) { if ( nFirstFree != SVT_NULL) { vVId.push_back( nFirstFree) ; m_vVert[nFirstFree] = m_vVert[nId] ; m_vVert[nId].nIdTria = SVT_DEL ; ++ nFirstFree ; } else vVId.push_back( nId) ; } else { if ( nFirstFree == SVT_NULL) nFirstFree = nId ; vVId.push_back( SVT_DEL) ; } } // se non c'è stata compattazione, esco if ( nFirstFree == SVT_NULL) return true ; // lunghezza vettore indici vertici int nVIdSize = int( vVId.size()) ; // aggiorno gli indici ai vertici dai 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]] ; } // ridimensiono l'array dei vertici m_vVert.resize( nFirstFree) ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::PackTriangles( void) { // compatto, sovrascrivendo eventuali triangoli cancellati INTVECTOR vTId ; vTId.reserve( GetTriangleSize()) ; int nFirstFree = SVT_NULL ; for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { if ( m_vTria[nId].nIdVert[0] != SVT_DEL) { if ( nFirstFree != SVT_NULL) { vTId.push_back( nFirstFree) ; m_vTria[nFirstFree] = m_vTria[nId] ; m_vTria[nId].nIdVert[0] = SVT_DEL ; ++ nFirstFree ; } else vTId.push_back( nId) ; } else { if ( nFirstFree == SVT_NULL) nFirstFree = nId ; vTId.push_back( SVT_DEL) ; } } // se non c'è stata compattazione, esco if ( nFirstFree == SVT_NULL) return true ; // Invalido HashGrid ResetHashGrids3d() ; // lunghezza vettore indici triangoli int nTIdSize = int( vTId.size()) ; // aggiorno gli indici ai triangoli dai vertici for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) { // recupero indice dal vertice a triangolo int nOId = m_vVert[nId].nIdTria ; // salto vertice cancellato if ( nOId == SVT_DEL) continue ; // verifico la validità dell'indice if ( nOId < 0 || nOId >= nTIdSize) return false ; // aggiorno m_vVert[nId].nIdTria = vTId[nOId] ; } // aggiorno gli indici ai triangoli dai triangoli (adiacenze) for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { // salto i triangoli cancellati if ( m_vTria[nId].nIdVert[0] == SVT_DEL) continue ; // recupero gli indici dei triangoli adiacenti int vOId[3] ; vOId[0] = m_vTria[nId].nIdAdjac[0] ; vOId[1] = m_vTria[nId].nIdAdjac[1] ; vOId[2] = m_vTria[nId].nIdAdjac[2] ; // aggiorno opportunamente ogni indice for ( int j = 0 ; j < 3 ; ++ j) { if ( vOId[j] == SVT_NULL) continue ; if ( vOId[j] < 0 || vOId[j] >= nTIdSize) return false ; m_vTria[nId].nIdAdjac[j] = ( vTId[vOId[j]] == SVT_DEL ? SVT_NULL : vTId[vOId[j]]) ; } } // aggiorno gli indici ai triangoli dalle facets for ( int nId = 0 ; m_bFaceted && nId < GetFacetSize() ; ++ nId) { // salto le facets non valide if ( m_vFacet[nId] == SVT_DEL) continue ; // verifico validità indice a triangolo if ( m_vFacet[nId] < 0 || m_vFacet[nId] >= nTIdSize) return false ; // aggiorno m_vFacet[nId] = vTId[m_vFacet[nId]] ; } // ridimensiono l'array dei triangoli m_vTria.resize( nFirstFree) ; 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::CreateByPolygonWithHoles( const POLYLINEVECTOR& vPL) { INTMATRIX vnPLIndMat ; vnPLIndMat.push_back( { 0}) ; for ( int i = 1 ; i < int( vPL.size()) ; ++ i) vnPLIndMat[0].push_back( i) ; return CreateByRegion( vPL, vnPLIndMat) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByRegion( const POLYLINEVECTOR& vPL, const INTMATRIX& vnPLIndMat) { // eseguo la triangolazione, dopo aver verificato che l'insieme di contorni costituisca una regione PNTVECTOR vPnt ; INTVECTOR vTria ; Triangulate Tri ; if ( ! Tri.MakeAdvanced( vPL, vPnt, vTria, vnPLIndMat)) 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() ; ResetHashGrids3d() ; // 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::CreateByPointCurve( const Point3d& ptP, const PolyLine& PL) { // verifico validità punto/polilinea bool bClosed = PL.IsClosed() ; // se chiusa, la polilinea deve avere almeno 3 punti if ( bClosed) { if ( PL.GetPointNbr() < 3) return false ; } // se aperta, almeno 2 else { if ( PL.GetPointNbr() < 2) return false ; } // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // costruisco la mesh int nVertNbr = 1 + PL.GetPointNbr() ; int nTriaNbr = nVertNbr - 2 ; if ( ! Init( nVertNbr, nTriaNbr)) return false ; int nIdV[3] ; // vertice comune a tutti i triangoli if ( ( nIdV[0] = AddVertex( ptP)) == SVT_NULL) return false ; // recupero il punto iniziale su curva 2 e lo inserisco come vertice Point3d ptP2 ; if ( ! PL.GetFirstPoint( ptP2)) return false ; if ( ( nIdV[1] = AddVertex( ptP2)) == SVT_NULL) return false ; // ciclo sui punti successivi della curva while ( PL.GetNextPoint( ptP2, bClosed)) { // recupero il punto e lo inserisco come vertice if ( ( nIdV[2] = AddVertex( ptP2)) == SVT_NULL) return false ; // inserisco il triangolo A2p -> A1p -> A1s if ( AddTriangle( nIdV) == SVT_NULL) return false ; // aggiorno indice punto precedente su curva nIdV[1] = nIdV[2] ; } // se chiusa aggiungo l'ultimo triangolo (non il vertice perchè è il primo della curva) if ( bClosed) { nIdV[2] = 1 ; // inserisco il triangolo A2p -> A1p -> A1s if ( AddTriangle( nIdV) == SVT_NULL) return false ; } // sistemo la topologia m_nStatus = TO_VERIFY ; return AdjustTopology() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nRuledType) { // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // se rigata a minima distanza tra le due curve if ( nRuledType == RLT_MINDIST) { // verifico ci siano almeno due punti diversi per curva if ( ( PL1.IsClosed() && PL1.GetPointNbr() < 3) || PL1.GetPointNbr() < 2) return false ; if ( ( PL2.IsClosed() && PL2.GetPointNbr() < 3) || PL2.GetPointNbr() < 2) return false ; // flag di curve entrambe chiuse e correzione conseguente a numero totale punti differenti bool bClosed = PL1.IsClosed() && PL2.IsClosed() ; // vettori punti con indice del primo punto dell'altra curva a distanza minima PNTIVECTOR vPnt1, vPnt2 ; bool bCommonInternalPoints ; if ( ! AssociatePolyLinesMinDistPoints( PL1, PL2, vPnt1, vPnt2, bCommonInternalPoints)) return false ; // verifico che non ci siano punti interni in comune if ( bCommonInternalPoints) return false ; int nTotP1 = int( vPnt1.size()) ; int nTotP2 = int( vPnt2.size()) ; // Costruisco la mesh int nVertNbr = nTotP1 + nTotP2 ; int nTriaNbr = max( nTotP1, nTotP2) + 1 ; if ( ! Init( nVertNbr, nTriaNbr)) return false ; // Recupero i punti iniziali sulla curva 1 int nV1p = -1 ; int nP1p = 0 ; int nV1s = -1 ; int nP1s = 1 ; bool bNext1 = ( nP1s < nTotP1) ; if ( ! bNext1) return false ; if ( ( nV1p = AddVertex( vPnt1[nP1p].first)) == SVT_NULL) return false ; // Recupero i punti iniziali sulla curva 2 int nV2p = -1 ; int nP2p = 0 ; int nV2s = -1 ; int nP2s = 1 ; bool bNext2 = ( nP2s < nTotP2) ; if ( ! bNext2) return false ; int nIdV[3] ; // se i punti iniziali non coincidono, inserisco il vertice iniziale di 2 if ( ! AreSamePointApprox( vPnt1[nP1p].first, vPnt2[nP2p].first)) { if ( ( nV2p = AddVertex( vPnt2[nP2p].first)) == SVT_NULL) return false ; } // altrimenti, inserisco un triangolo e mi sposto in avanti su entrambe le curve else { // inserisco il vertice V1s if ( ( nV1s = AddVertex( vPnt1[nP1s].first)) == SVT_NULL) return false ; // inserisco il vertice V2s if ( ( nV2s = AddVertex( vPnt2[nP2s].first)) == SVT_NULL) return false ; // inserisco il triangolo V1p -> V1s -> V2s nIdV[0] = nV1p ; nIdV[1] = nV1s ; nIdV[2] = nV2s ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; // passo al punto successivo su 1 nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ; bNext1 = ( nP1s < nTotP1) ; // passo al punto successivo su 2 nV2p = nV2s ; nP2p = nP2s ; ++ nP2s ; bNext2 = ( nP2s < nTotP2) ; } // ciclo sui punti while ( bNext1 || bNext2) { // se non c'è V2s oppure c'è nuovo V1s e la diagonale più corta è V2p -> V1s if ( ! bNext2 || ( bNext1 && ( nP1s == vPnt2[nP2p].second || vPnt1[nP1s].second == nP2p))) { // inserisco il vertice V1s (se ultimo e curve chiuse, prendo il primo) if ( nP1s == nTotP1 - 1 && bClosed) nV1s = 0 ; else if ( ( nV1s = AddVertex( vPnt1[nP1s].first)) == SVT_NULL) return false ; // inserisco il triangolo V2p -> V1p -> V1s 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 ( bNext2 && AreSamePointApprox( vPnt1[nP1s].first, vPnt2[nP2s].first)) { nV2p = nV2s ; nP2p = nP2s ; ++ nP2s ; bNext2 = ( nP2s < nTotP2) ; } // passo al punto successivo su 1 nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ; bNext1 = ( nP1s < nTotP1) ; } // altrimenti è V1p -> V2s else { // inserisco il vertice V2s (se ultimo e curve chiuse, prendo il primo) if ( nP2s == nTotP2 - 1 && bClosed) nV2s = 1 ; else if ( ( nV2s = AddVertex( vPnt2[nP2s].first)) == SVT_NULL) return false ; // inserisco il triangolo V2p -> V1p -> V2s 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 ( bNext1 && AreSamePointApprox( vPnt1[nP1s].first, vPnt2[nP2s].first)) { nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ; bNext1 = ( nP1s < nTotP1) ; } // passo al punto successivo su 2 nV2p = nV2s ; nP2p = nP2s ; ++ nP2s ; bNext2 = ( nP2s < nTotP2) ; } } } // altrimenti rigata con parametrizzazione sincrona sulle due curve else { // verifico validità polilinee (devono avere almeno 2 punti e non coincidere se non agli estremi aperti) if ( ! VerifyPolylinesForTwoCurves( PL1, PL2)) return false ; // 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 bool bTwist = false ; int nVertNbr = PL1.GetPointNbr() + PL2.GetPointNbr() ; int nTriaNbr = max( PL1.GetPointNbr(), PL2.GetPointNbr()) + 1 ; if ( ! Init( nVertNbr, nTriaNbr)) return false ; // recupero i punti iniziali su curva 1 int nV1p = SVT_NULL ; double dU1p = 0 ; Point3d ptP1p ; int nV1s = SVT_NULL ; double dU1s = 0 ; Point3d ptP1s ; bool bNext1 = PL1.GetFirstUPoint( &dU1p, &ptP1p) && PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ; if ( ! bNext1) return false ; double dA1p = 0 ; double dA1s = ( dU1s - dU1F) / dDeltaU1 ; if ( ( nV1p = AddVertex( ptP1p)) == SVT_NULL) return false ; // recupero i punti iniziali su curva 2 int nV2p = SVT_NULL ; double dU2p = 0 ; Point3d ptP2p ; int nV2s = SVT_NULL ; double dU2s = 0 ; Point3d ptP2s ; bool bNext2 = PL2.GetFirstUPoint( &dU2p, &ptP2p) && PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ; if ( ! bNext2) return false ; double dA2p = 0 ; double 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 richiesto smoothing, ci sono entrambi i successivi, hanno circa lo stesso parametro e i segmenti sono sghembi oltre il limite double dDiagDist = 0 ; if ( nRuledType == RLT_ISOPAR_SMOOTH && bNext1 && bNext2 && abs( dA1p + dA1s - dA2p - dA2s) < min( dA1s - dA1p, dA2s - dA2p) && DistLineLine( ptP1p, ptP2s, ptP2p, ptP1s).GetDist( dDiagDist) && dDiagDist > STM_TWIST_DIAG_DIST) { bTwist = true ; // 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 un nuovo vertice punto medio della linea tra i punti medi Point3d ptCen = ( ptP1s + ptP1p + ptP2s + ptP2p) / 4 ; int nVCen = AddVertex( ptCen) ; if ( nVCen == SVT_NULL) return false ; // creo 4 triangoli dai lati del quadrilatero al vertice nIdV[0] = nV2p ; nIdV[1] = nV1p ; nIdV[2] = nVCen ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; nIdV[0] = nV1p ; nIdV[1] = nV1s ; nIdV[2] = nVCen ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; nIdV[0] = nV1s ; nIdV[1] = nV2s ; nIdV[2] = nVCen ; if ( AddTriangle( nIdV) == SVT_NULL) return false ; nIdV[0] = nV2s ; nIdV[1] = nV2p ; nIdV[2] = nVCen ; 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 ; } // se non c'è dA2s oppure c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s else if ( ! bNext2 || ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM)) { // 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 ; } } // in presenza di twist aumento il limite sulla deviazione angolare if ( bTwist) SetSmoothAngle( STM_TWIST_SMOOTH_ANG) ; } // 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 dU1p = 0 ; Point3d ptP1p ; double dU1s = 0 ; Point3d ptP1s ; bool bNext1 = PL1.GetFirstUPoint( &dU1p, &ptP1p) && PL1.GetNextUPoint( &dU1s, &ptP1s, true) ; if ( ! bNext1) return true ; double dA1p = 0 ; double dA1s = ( dU1s - dU1F) / dDeltaU1 ; double dU2s = 0 ; Point3d ptP2s ; double dU2p = 0 ; Point3d ptP2p ; bool bNext2 = PL2.GetFirstUPoint( &dU2p, &ptP2p) && PL2.GetNextUPoint( &dU2s, &ptP2s, true) ; if ( ! bNext2) return true ; double dA2p = 0 ; double 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) ; } //---------------------------------------------------------------------------- static bool VeryfyPolylineForRevolution( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx) { // 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 // se la curva è chiusa oppure è vicina all'asse ( entro EPS_SMALL), ma non ho che sono start o end ad essere sull'asse // allora dovrò rimaneggiare la polyline 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 ; } //---------------------------------------------------------------------------- static bool AdjustPolylineForRevolution( PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx) { Vector3d vtAxN = vtAx ; vtAxN.Normalize() ; // determino il piano della polilinea int nRank ; Point3d ptCen ; Vector3d vtScN ; if ( ! PL.IsFlat( nRank, ptCen, vtScN, 10 * EPS_SMALL) || nRank == 0) return false ; Plane3d plSect ; if ( nRank >= 2) plSect.Set( ptCen, vtScN) ; else { if ( ! plSect.Set( ptCen, vtAxN ^ vtScN)) return false ; } // verifico che l'asse giaccia nel piano if ( ! PointInPlaneApprox( ptAx, plSect) || ! PointInPlaneApprox( ptAx + 100 * vtAxN, plSect)) return false ; // calcolo il piano perpendicolare a quello della curva e contenente l'asse Vector3d vtTrN = ( ptCen - ptAx) - ( ptCen - ptAx) * vtAxN * vtAxN ; if ( ! vtTrN.Normalize()) { vtTrN = vtAxN ^ vtScN ; vtTrN.Normalize() ; } Plane3d plTrim ; plTrim.Set( ptAx, vtTrN) ; // Trimmo la polilinea con il piano (leggermente spostato) const double DIST_SIC = 5 * EPS_SMALL ; plTrim.Translate( DIST_SIC * vtTrN) ; PL.Trim( plTrim, false) ; // Se polilinea risultante è aperta con estremità molto vicine all'asse le porto su questo if ( ! PL.IsClosed()) { // verifico l'inizio double dUStart ; Point3d ptStart ; PL.GetFirstUPoint( &dUStart, &ptStart) ; DistPointLine dStAx( ptStart, ptAx, vtAx, 1, false) ; double dStDist ; if ( dStAx.GetDist( dStDist) && dStDist < 1.1 * DIST_SIC) { dStAx.GetMinDistPoint( ptStart) ; PL.EraseFirstUPoint() ; PL.AddUPoint( dUStart, ptStart, false) ; } // verifico la fine double dUEnd ; Point3d ptEnd ; PL.GetLastUPoint( &dUEnd, &ptEnd) ; DistPointLine dEnAx( ptEnd, ptAx, vtAx, 1, false) ; double dEnDist ; if ( dEnAx.GetDist( dEnDist) && dEnDist < 1.1 * DIST_SIC) { dEnAx.GetMinDistPoint( ptEnd) ; PL.EraseLastUPoint() ; PL.AddUPoint( dUEnd, ptEnd, true) ; } } return true ; } //---------------------------------------------------------------------------- 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 = ( abs( dMove) < EPS_SMALL) ; // verifico che l'angolo di rotazione sia significativo e, se solo rivoluzione, non superi un giro if ( abs( dAngRot) < EPS_ANG_SMALL) return false ; if ( bOnlyRev && abs( dAngRot) > ANG_FULL) dAngRot = _copysign( ANG_FULL, dAngRot) ; // verifico se rotazione completa bool bFullRev = bOnlyRev && ( abs( abs( 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 ( abs( dStepRot) < MIN_STEP_ROT) dStepRot = _copysign( MIN_STEP_ROT, dAngRot) ; else if ( abs( 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 PolyLine MyPL = PL ; if ( ! VeryfyPolylineForRevolution( MyPL, ptAx, vtAx) && ! AdjustPolylineForRevolution( MyPL, ptAx, vtAx)) { LOG_ERROR( GetEGkLogger(), "StmCreateByRevolution : polyline inside meets axis") return false ; } // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // verifico se la polilinea è chiusa bool bClosed = MyPL.IsClosed() ; // costruisco la mesh int nPointNbr = MyPL.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 ( ! MyPL.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 ( MyPL.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::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( double dTol) { // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // definisco un Grid per i vertici della superficie PointGrid3d VertGrid ; int nBuckets = GetVertexSize() ; VertGrid.Init( nBuckets, max( dTol, 100 * EPS_SMALL)) ; // 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, dTol, nAliasId)) { VertGrid.InsertPoint( ptP, nId) ; // salvo l'Id nel vettore di reindirizzo vVId.push_back( nId) ; } // c'è 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 INTVECTOR vInvalidIds ; for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { // salto i triangoli cancellati if ( m_vTria[nId].nIdVert[0] == SVT_DEL) continue ; // recupero gli indici dei vertici del triangolo int vOId[3]{ m_vTria[nId].nIdVert[0], m_vTria[nId].nIdVert[1], m_vTria[nId].nIdVert[2]} ; // 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]] ; // verifico se triangolo da rimuovere per vertici coincidenti bool bRemove = false ; int nTAdj1 = SVT_NULL, nTAdj2 = SVT_NULL ; if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[1]) { nTAdj1 = m_vTria[nId].nIdAdjac[1] ; nTAdj2 = m_vTria[nId].nIdAdjac[2] ; bRemove = true ; } else if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[2]) { nTAdj1 = m_vTria[nId].nIdAdjac[0] ; nTAdj2 = m_vTria[nId].nIdAdjac[1] ; bRemove = true ; } else if ( m_vTria[nId].nIdVert[1] == m_vTria[nId].nIdVert[2]) { nTAdj1 = m_vTria[nId].nIdAdjac[0] ; nTAdj2 = m_vTria[nId].nIdAdjac[2] ; bRemove = true ; } if ( bRemove) { // sistemo le contro adiacenze if ( nTAdj1 != SVT_NULL) { for ( int j = 0 ; j < 3 ; ++ j) if ( m_vTria[nTAdj1].nIdAdjac[j] == nId) m_vTria[nTAdj1].nIdAdjac[j] = nTAdj2 ; } if ( nTAdj2 != SVT_NULL) { for ( int j = 0 ; j < 3 ; ++ j) if ( m_vTria[nTAdj2].nIdAdjac[j] == nId) m_vTria[nTAdj2].nIdAdjac[j] = nTAdj1 ; } // rimuovo il triangolo RemoveTriangle( nId) ; } // verifico se il triangolo va rimosso per normale non calcolabile else if ( ! CalcTriangleNormal( nId)) vInvalidIds.emplace_back( nId) ; } // elimino triangoli invalidi ( con gestione speciale per evitare T-junctions) if ( ! vInvalidIds.empty()) RemoveInvalidTriangles( vInvalidIds) ; // compatto il vettore dei vertici if ( ! PackVertices()) return false ; // ricalcolo le adiacenze m_nStatus = TO_VERIFY ; if ( ! AdjustTopology()) return false ; // compatto il vettore dei triangoli return PackTriangles() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::DoSewing( const ISurfTriMesh& stmOther, const Frame3d& frOther, double dTol) { // recupero l'altra superficie const SurfTriMesh* pOther = GetBasicSurfTriMesh( &stmOther) ; if ( pOther == nullptr) return false ; // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; // definisco un Grid per i vertici delle due superfici PointGrid3d VertGrid ; int nBuckets = max( GetVertexSize() + pOther->GetVertexSize(), 1000) ; 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, dTol, 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()) ; // aggiungo 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]] ; // se triangolo ancora valido, lo inserisco if ( vId[0] != vId[1] && vId[0] != vId[2] && vId[1] != vId[2]) { if ( AddTriangle( vId) == SVT_NULL) return false ; } } // compatto il vettore dei vertici if ( ! PackVertices()) return false ; // ricalcolo le adiacenze m_nStatus = TO_VERIFY ; if ( ! AdjustTopology()) return false ; // compatto il vettore dei triangoli return PackTriangles() ; } //---------------------------------------------------------------------------- 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 e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // 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 e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // 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 ( abs( dCoeffX) < EPS_ZERO && abs( dCoeffY) < EPS_ZERO && abs( dCoeffZ) < EPS_ZERO) return false ; // calcolo bbox allineato con riferimento di scalatura e senza tener conto dello spessore // lo scalo per verificare se tutto si riduce a un punto o una linea (solo se diretta come assi ref) BBox3d b3Ref ; if ( ! GetBBox( frRef, b3Ref)) return false ; Vector3d vtDelta = b3Ref.GetMax() - b3Ref.GetMin() ; bool bZeroX = ( abs( vtDelta.x * dCoeffX) < EPS_SMALL) ; bool bZeroY = ( abs( vtDelta.y * dCoeffY) < EPS_SMALL) ; bool bZeroZ = ( abs( vtDelta.z * dCoeffZ) < EPS_SMALL) ; if ( ( bZeroX && bZeroY) || ( bZeroX && bZeroZ) || ( bZeroY && bZeroZ)) return false ; // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // 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) ; } // 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)) ; // se c'è mirror, devo invertire le facce if ( bMirror) { for ( int i = 0 ; i < GetTriangleSize() ; ++ i) if ( m_vTria[i].nIdVert[0] != SVT_DEL) InvertTriangle( i) ; } // rimuovo i triangoli resi invalidi dalla scalatura bool bOk = DoCompacting() ; // rimuovo i triangoli doppi bool bModified = false ; bOk = bOk && RemoveDoubleTriangles( bModified) ; if ( bModified) bOk = bOk && ( AdjustVertices() && DoCompacting()) ; return bOk ; } //---------------------------------------------------------------------------- bool SurfTriMesh::InvertTriangle( int nT) { // controllo validità triangolo if ( ! ExistsTriangle( nT)) return true ; // 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 true ; // 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 e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // 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) { // 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 e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // 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 i triangoli for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { // 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 ; // se frame identità, non devo fare alcunché if ( IsGlobFrame( frRef)) return true ; // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // trasformo i vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.ToGlob( frRef) ; } // trasformo le normali dei triangoli for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) m_vTria[i].vtN.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 ; // se frame identità, non devo fare alcunché if ( IsGlobFrame( frRef)) return true ; // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // trasformo i vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.ToLoc( frRef) ; } // trasformo le normali dei triangoli for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) m_vTria[i].vtN.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 e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // trasformo i vertici for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria != SVT_DEL) m_vVert[i].ptP.LocToLoc( frOri, frDest) ; } // trasformo le normali dei triangoli for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) m_vTria[i].vtN.LocToLoc( frOri, frDest) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::Invert( void) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // imposto ricalcolo numero delle parti (le shell non cambiano) m_vPart.clear() ; // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // inverto i triangoli for ( int i = 0 ; i < GetTriangleSize() ; ++ i) InvertTriangle( i) ; // se bordi della sfaccettatura validi if ( m_bFacEdged) { for ( int nE = 0 ; nE < GetEdgeCount() ; ++ nE) { // inversione dei vertici nel vettore delle sfaccettature swap( m_vFacEdge[nE].nIdVert[0], m_vFacEdge[nE].nIdVert[1]) ; // inversione delle adiacenze nel vettore delle sfaccettature swap( m_vFacEdge[nE].nIdFacAdj[0], m_vFacEdge[nE].nIdFacAdj[1]) ; // inversione dell'angolo interno m_vFacEdge[nE].dIntAng *= -1. ; } } return true ; } //---------------------------------------------------------------------------- void SurfTriMesh::ResetHashGrids3d( void) const { if ( m_pHGrd3d != nullptr) { delete m_pHGrd3d ; m_pHGrd3d = nullptr ; m_b3HGrd3d.Reset() ; } } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyHashGrids3d( void) const { // se già calcolato, non devo fare altro if ( m_pHGrd3d != nullptr) return true ; // alloco m_pHGrd3d = new HashGrids3d ; if ( m_pHGrd3d == nullptr) return false ; // riempio const int LIM_HG_TRIA = 127 ; m_pHGrd3d->SetActivationGrid( GetTriangleCount() > LIM_HG_TRIA) ; Triangle3d TriaH ; int nT = GetFirstTriangle( TriaH) ; while ( nT != SVT_NULL) { BBox3d boxT ; TriaH.GetLocalBBox( boxT) ; m_b3HGrd3d.Add( boxT) ; if ( ! m_pHGrd3d->Add( nT, boxT)) { ResetHashGrids3d() ; return false ; } nT = GetNextTriangle( nT, TriaH) ; } if ( ! m_pHGrd3d->Update()) { ResetHashGrids3d() ; return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetAllTriaOverlapBox( const BBox3d& b3Box, INTVECTOR& vT) const { if ( ! VerifyHashGrids3d()) return false ; return m_pHGrd3d->Find( b3Box, vT) ; } //---------------------------------------------------------------------------- const BBox3d& SurfTriMesh::GetAllTriaBox( void) const { VerifyHashGrids3d() ; return m_b3HGrd3d ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyConnection( bool bShellsAndParts) const { if ( ! IsValid()) return false ; // se non sono già note le shell if ( m_nShells == -1) { // reset connessione for ( auto& Tria : m_vTria) Tria.nShell = SVT_NULL ; // ciclo sui triangoli per determinare le shells m_nShells = 0 ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { // salto triangoli cancellati o già assegnati if ( m_vTria[i].nIdVert[0] == SVT_DEL || m_vTria[i].nShell != SVT_NULL) continue ; // assegno indice di shell connessa al triangolo m_vTria[i].nShell = m_nShells ; ++ m_nShells ; // set di triangoli da aggiornare set stTria ; stTria.insert( i) ; while ( ! stTria.empty()) { // tolgo un triangolo dal set const auto iIt = stTria.begin() ; int nT = *iIt ; stTria.erase( iIt) ; // aggiorno i triangoli adiacenti for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT != SVT_NULL && m_vTria[nAdjT].nShell == SVT_NULL) { m_vTria[nAdjT].nShell = m_vTria[nT].nShell ; stTria.insert( nAdjT) ; } } } } } // reset delle parti m_vPart.clear() ; // se richiesta solo la determinazione delle shell, esco if ( ! bShellsAndParts) return true ; // Se superficie vuota, allora non ho parti if ( m_nShells == 0) return true ; // Se ho solo una shell, allora ho una sola parte if ( m_nShells == 1) { m_vPart.emplace_back( m_bClosed, INTVECTOR{ 0}) ; return true ; } // Ho più shell devo controllare la loro posizione relativa struct SHELLINFO { SHELLINFO( int nI, double dVol, const BBox3d& b3B, ISurfTriMesh* pStm) : nInd( nI), dVolume( dVol), b3Box( b3B), pStmShell( pStm) {} int nInd ; double dVolume ; BBox3d b3Box ; PtrOwner pStmShell ; } ; vector vOuterShells ; vector vInnerShells ; INTVECTOR vOpenShells ; for ( int nSh = 0 ; nSh < m_nShells ; ++ nSh) { // se la shell è chiusa if ( IsShellClosed( nSh)) { // creo una superficie clonata dalla shell PtrOwner pStmShell( CloneShell( nSh)) ; if ( IsNull( pStmShell) || ! pStmShell->IsValid()) return false ; // ne calcolo il volume (con segno) double dVol = 0. ; pStmShell->GetVolume( dVol) ; // ne calcolo il bounding box BBox3d b3Box ; pStmShell->GetLocalBBox( b3Box, BBF_STANDARD) ; // la inserisco nel vettore opportuno if ( dVol > 0) vOuterShells.emplace_back( nSh, dVol, b3Box, Release( pStmShell)) ; else vInnerShells.emplace_back( nSh, dVol, b3Box, Release( pStmShell)) ; } // altrimenti aperta else { // la inserisco nel vettore delle shell aperte vOpenShells.push_back( nSh) ; } } // ordino il vettore delle shell esterne in senso crescente di volume (crescenti anche in valore assoluto) sort( vOuterShells.begin(), vOuterShells.end(), []( const SHELLINFO& a, const SHELLINFO& b) { return ( a.dVolume < b.dVolume) ; }) ; // ordino il vettore delle shell interne in senso crescente di volume (decrescenti in valore assoluto) sort( vInnerShells.begin(), vInnerShells.end(), []( const SHELLINFO& a, const SHELLINFO& b) { return ( a.dVolume < b.dVolume) ; }) ; // classifico le shell esterne for ( int i = 0 ; i < int( vOuterShells.size()) ; ++ i) { // inserisco nel vettore delle parti m_vPart.emplace_back( true, INTVECTOR{ vOuterShells[i].nInd}) ; // cerco eventuali interne che vi appartengono CISURFTMPVECTOR vStmTest{ vOuterShells[i].pStmShell} ; for ( int j = 0 ; j < int( vInnerShells.size()) ; ++ j) { // se libera e il box è incluso, verifico se realmente interna if ( vInnerShells[j].nInd >= 0 && vOuterShells[i].b3Box.Encloses( vInnerShells[j].b3Box)) { Point3d ptCheck ; vInnerShells[j].pStmShell->GetFirstVertex( ptCheck) ; bool bIsInside = true ; for ( const auto& pStmTest : vStmTest) { BBox3d b3Test = pStmTest->GetAllTriaBox() ; if ( b3Test.Overlaps( vInnerShells[j].b3Box)) { DistPointSurfTm distCalculator( ptCheck, *pStmTest) ; if ( ! distCalculator.IsPointInside()) { bIsInside = false ; break ; } } } if ( bIsInside) { m_vPart.back().vShell.push_back( vInnerShells[j].nInd) ; vInnerShells[j].nInd = -1 ; vStmTest.push_back( vInnerShells[j].pStmShell) ; } } } } // classifico le shell interne rimaste libere bool bNewInner = true ; CISURFTMPVECTOR vStmTest ; for ( int i = 0 ; i < int( vInnerShells.size()) ; ++ i) { if ( vInnerShells[i].nInd >= 0) { // se non è nuova interna verifico sia interna alle altre correnti if ( ! bNewInner) { bool bIsInside = true ; Point3d ptCheck ; vInnerShells[i].pStmShell->GetFirstVertex( ptCheck) ; // se il box interferisce con altri, verifico se realmente interna for ( const auto& pStmTest : vStmTest) { BBox3d b3Test = pStmTest->GetAllTriaBox() ; if ( b3Test.Overlaps( vInnerShells[i].b3Box)) { DistPointSurfTm distCalculator( ptCheck, *pStmTest) ; if ( ! distCalculator.IsPointInside()) { bIsInside = false ; break ; } } } if ( bIsInside) m_vPart.back().vShell.push_back( vInnerShells[i].nInd) ; else bNewInner = true ; } // se nuova interna if ( bNewInner) { bNewInner = false ; m_vPart.emplace_back( true, INTVECTOR{ -1, vInnerShells[i].nInd}) ; vStmTest.clear() ; } vInnerShells[i].nInd = -1 ; vStmTest.push_back( vInnerShells[i].pStmShell) ; } } // aggiungo all'elenco delle parti le shell aperte for ( int i = 0 ; i < int( vOpenShells.size()) ; ++ i) { m_vPart.emplace_back( false, INTVECTOR{ vOpenShells[i]}) ; } return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetShellCount( void) const { if ( ! IsValid()) return 0 ; if ( m_nShells == -1 && ! VerifyConnection()) return 0 ; return m_nShells ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetShellArea( int nShell, double& dArea) const { // Controllo parametro di ritorno if ( &dArea == nullptr) return false ; // Imposto area nulla dArea = 0 ; // Verifiche sull'oggetto if ( ! IsValid()) return false ; if ( m_nShells == -1 && ! VerifyConnection()) return false ; // Se la componente non esiste, errore if ( nShell < 0 || nShell >= m_nShells) return false ; // sommo l'area di tutti i triangoli della shell Triangle3d Tria ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { if ( m_vTria[nId].nShell == nShell) dArea += Tria.GetArea() ; nId = GetNextTriangle( nId, Tria) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::IsShellClosed( int nShell) const { // Verifiche sull'oggetto if ( ! IsValid()) return false ; if ( m_nShells == -1 && ! VerifyConnection( false)) return false ; // Se la componente non esiste, errore if ( nShell < 0 || nShell >= m_nShells) return false ; // ciclo sui triangoli della shell bool bClosed = true ; for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { // se triangolo non cancellato e della shell if ( m_vTria[i].nIdVert[0] != SVT_DEL && m_vTria[i].nShell == nShell) { // 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 ; break ; } } } // restituisco il risultato return bClosed ; } //---------------------------------------------------------------------------- bool SurfTriMesh::RemoveShell( int nShell) { if ( ! IsValid()) return false ; // Il numero delle componenti deve essere maggiore di zero o calcolabile if ( m_nShells == -1 && ! VerifyConnection()) return false ; // Se la componente non esiste, errore if ( nShell < 0 || nShell >= m_nShells) return false ; // Rimuovo i triangoli della componente nShell for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { if ( m_vTria[i].nShell == nShell) RemoveTriangle( i) ; } // I dati di Shells e Parts sono stati resettati da RemoveTriangle // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; return true ; } //---------------------------------------------------------------------------- SurfTriMesh* SurfTriMesh::CloneShell( int nShell) const { if ( ! IsValid()) return nullptr ; // Il numero delle componenti deve essere maggiore di zero o calcolabile if ( m_nShells == -1 && ! VerifyConnection()) return nullptr ; // Se la componente non esiste, errore if ( nShell < 0 || nShell >= m_nShells) return nullptr ; // Creo nuovo oggetto SurfTriMesh PtrOwner pSurfTM( new( nothrow) SurfTriMesh) ; if ( IsNull( pSurfTM)) return nullptr ; // Copio il valore dei membri pSurfTM->m_dLinTol = m_dLinTol ; pSurfTM->m_dBoundaryAng = m_dBoundaryAng ; pSurfTM->m_dCosBndAng = m_dCosBndAng ; pSurfTM->m_dSmoothAng = m_dSmoothAng ; pSurfTM->m_dCosSmAng = m_dCosSmAng ; // Copio i triangoli for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL && m_vTria[i].nShell == nShell) { int nNewInd[3] = { pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[0]].ptP), pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[1]].ptP), pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[2]].ptP)} ; if ( pSurfTM->AddTriangle( nNewInd, m_vTria[i].nTFlag) == SVT_NULL) return nullptr ; } } // Aggiusto la superficie pSurfTM->DoCompacting() ; // Restituisco la nuova superficie return Release( pSurfTM) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetPartLocalBBox( int nP, BBox3d& b3Loc) const { // verifico esistenza della parte if ( nP < 0 || nP >= GetPartCount()) return false ; // assegno il box in locale b3Loc.Reset() ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { // recupero il vettore delle Shell della parte corrente const auto& vShell = m_vPart[nP].vShell ; // scorro i triangoli validi contenuti nella Shell if ( m_vTria[i].nIdVert[0] != SVT_DEL && find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) { // ciclo sui tre vertici for ( int j = 0 ; j < 3 ; ++ j) b3Loc.Add( m_vVert[m_vTria[i].nIdVert[j]].ptP) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetPartBBox( int nP, const Frame3d& frRef, BBox3d& b3Ref) const { // verifico esistenza della parte if ( nP < 0 || nP >= GetPartCount()) return false ; // assegno il box in locale b3Ref.Reset() ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { // recupero il vettore delle Shell della parte corrente const auto& vShell = m_vPart[nP].vShell ; // scorro i triangoli validi contenuti nella Shell if ( m_vTria[i].nIdVert[0] != SVT_DEL && find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) { // ciclo sui tre vertici for ( int j = 0 ; j < 3 ; ++ j) b3Ref.Add( GetToGlob( m_vVert[m_vTria[i].nIdVert[j]].ptP, frRef)) ; } } return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetPartCount( void) const { if ( ! IsValid()) return 0 ; if ( m_vPart.empty() && ! VerifyConnection()) return 0 ; return int( m_vPart.size()) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetPartArea( int nPart, double& dArea) const { // Controllo parametro di ritorno if ( &dArea == nullptr) return false ; // Imposto area nulla dArea = 0 ; // Verifiche sull'oggetto if ( ! IsValid()) return false ; if ( m_vPart.empty() && ! VerifyConnection()) return false ; // Se la componente non esiste, errore if ( nPart < 0 || nPart >= int( m_vPart.size())) return false ; // sommo l'area di tutti i triangoli della parte Triangle3d Tria ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { const auto& vShell = m_vPart[nPart].vShell ; if ( find( vShell.begin(), vShell.end(), m_vTria[nId].nShell) != vShell.end()) dArea += Tria.GetArea() ; nId = GetNextTriangle( nId, Tria) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::IsPartClosed( int nPart) const { // Verifiche sull'oggetto if ( ! IsValid()) return false ; if ( m_vPart.empty() && ! VerifyConnection()) return false ; // Se la componente non esiste, errore if ( nPart < 0 || nPart >= int( m_vPart.size())) return false ; return m_vPart[nPart].bClosed ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetPartVolume( int nPart, double& dVolume) const { // Controllo parametro di ritorno if ( &dVolume == nullptr) return false ; // Imposto volume nullo dVolume = 0 ; // Verifiche sull'oggetto if ( ! IsValid()) return false ; if ( m_vPart.empty() && ! VerifyConnection()) return false ; // Se la componente non esiste, errore if ( nPart < 0 || nPart >= int( m_vPart.size())) return false ; // la parte deve essere chiusa if ( ! m_vPart[nPart].bClosed) return true ; // sommo il volume con segno di tutte le piramidi della parte dall'origine ad ogni faccia Triangle3d Tria ; int nId = GetFirstTriangle( Tria) ; while ( nId != SVT_NULL) { const auto& vShell = m_vPart[nPart].vShell ; if ( find( vShell.begin(), vShell.end(), m_vTria[nId].nShell) != vShell.end()) { 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::GetPartLoops( int nPart, POLYLINEVECTOR& vPL) const { // verifico lo stato if ( m_nStatus != OK) return false ; vPL.clear() ; // aggiorno timestamp dei triangoli ++ m_nTimeStamp ; for ( auto& Tria : m_vTria) Tria.nTemp = m_nTimeStamp ; // incremento time stamp ++ m_nTimeStamp ; // recupero il vettore delle Shell che compongono la part const auto& vShell = m_vPart[nPart].vShell ; // ciclo sui triangoli for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) { // se triangolo valido e non ancora visitato if ( m_vTria[nT].nIdVert[0] != SVT_DEL && m_vTria[nT].nTemp != m_nTimeStamp && find( vShell.begin(), vShell.end(), m_vTria[nT].nShell) != vShell.end()) { // determino i triangoli adiacenti int nAdjT[3] ; for ( int j = 0 ; j < 3 ; ++ j) nAdjT[j] = m_vTria[nT].nIdAdjac[j] ; // se tutti e tre i lati sono di contorno if ( nAdjT[0] == SVT_NULL && nAdjT[1] == SVT_NULL && nAdjT[2] == SVT_NULL) { // ho trovato un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } // se i due lati 0 e 1 sono di contorno else if ( nAdjT[0] == SVT_NULL && nAdjT[1] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 1 e 2 sono di contorno else if ( nAdjT[1] == SVT_NULL && nAdjT[2] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 2 e 0 sono di contorno else if ( nAdjT[2] == SVT_NULL && nAdjT[0] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 0 è di contorno else if ( nAdjT[0] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 1 è di contorno else if ( nAdjT[1] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se il lato 2 è di contorno else if ( nAdjT[2] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; // cammino lungo il loop fino a chiuderlo if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back())) return false ; } // altrimenti non c'è contorno else { // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::RemovePart( int nPart) { if ( ! IsValid()) return false ; // Il numero delle componenti deve essere maggiore di zero o calcolabile if ( m_vPart.empty() && ! VerifyConnection()) return false ; // Se la componente non esiste, errore if ( nPart < 0 || nPart >= int( m_vPart.size())) return false ; // Rimuovo i triangoli della componente nPart (ovvero delle sue shell) const auto vShell = m_vPart[nPart].vShell ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { if ( find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) RemoveTriangle( i) ; } // I dati di Shells e Parts sono stati resettati da RemoveTriangle // imposto ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; return true ; } //---------------------------------------------------------------------------- SurfTriMesh* SurfTriMesh::ClonePart( int nPart) const { if ( ! IsValid()) return nullptr ; // Il numero delle parti deve essere maggiore di zero o calcolabile if ( m_vPart.empty() && ! VerifyConnection()) return nullptr ; // Se la parte non esiste, errore if ( nPart < 0 || nPart >= int( m_vPart.size())) return nullptr ; // Creo nuovo oggetto SurfTriMesh PtrOwner pSurfTM( new( nothrow) SurfTriMesh) ; if ( IsNull( pSurfTM)) return nullptr ; // Copio il valore dei membri pSurfTM->m_dLinTol = m_dLinTol ; pSurfTM->m_dBoundaryAng = m_dBoundaryAng ; pSurfTM->m_dCosBndAng = m_dCosBndAng ; pSurfTM->m_dSmoothAng = m_dSmoothAng ; pSurfTM->m_dCosSmAng = m_dCosSmAng ; // Copio i triangoli for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { const auto& vShell = m_vPart[nPart].vShell ; if ( m_vTria[i].nIdVert[0] != SVT_DEL && find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) { int nNewInd[3] = { pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[0]].ptP), pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[1]].ptP), pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[2]].ptP)} ; if ( pSurfTM->AddTriangle( nNewInd, m_vTria[i].nTFlag) == SVT_NULL) return nullptr ; } } // Aggiusto la superficie pSurfTM->DoCompacting() ; // Restituisco la nuova superficie return Release( pSurfTM) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetPartAndShellFromFacet( int nFacet, int& nPart, int& nShell) const { // l'indice della faccia deve essere nei limiti if ( nFacet < 0 || nFacet >= int( m_vFacet.size())) return false ; // mi assicuro che siano calcolate il numero di parti e di shell int nParts = GetPartCount() ; int nTria = m_vFacet[nFacet] ; nShell = m_vTria[nTria].nShell ; // scopro in quale part è la shell int nPartTemp = 0 ; nPart = - 1 ; while ( nPartTemp < nParts && nPart == - 1) { for ( int i : m_vPart[nPartTemp].vShell) { if ( i == nShell) { nPart = nPartTemp ; break ; } } ++nPartTemp ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::ResetTFlags( void) { for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) m_vTria[i].nTFlag = 0 ; m_nMaxTFlag = 0 ; m_OGrMgr.Clear() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::ResetTempInts( void) const { for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) m_vTria[i].nTemp = 0 ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetTFlag( int nId, int nTFlag) { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; m_vTria[nId].nTFlag = nTFlag ; m_nMaxTFlag = max( m_nMaxTFlag, abs( nTFlag)) ; m_OGrMgr.Clear() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetTempInt( int nId, int nTempInt) const { // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL) return false ; m_vTria[nId].nTemp = nTempInt ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol) { // scorro i triangoli for ( const Triangle3dEx& Tria : vTria) { // ciclo sui tre vertici int nIdV[3]{} ; for ( int nV = 0 ; nV < 3 ; ++ nV) { // verifico se vertice già presente int nId ; if ( ! VertGrid.Find( Tria.GetP( nV), dVertexTol, nId)) { // se non presente, lo aggiugo nIdV[nV] = AddVertex( Tria.GetP( nV)) ; if ( nIdV[nV] == SVT_NULL) return false ; VertGrid.InsertPoint( Tria.GetP( nV), nIdV[nV]) ; } else nIdV[nV] = nId ; } // se i vertici sono tutti diversi tra loro, inserisco il triangolo if ( nIdV[0] != nIdV[1] && nIdV[0] != nIdV[2] && nIdV[1] != nIdV[2]) { int nT = AddTriangle( nIdV, Tria.GetGrade()) ; if ( nT != SVT_NULL && nT != SVT_DEL) { // associo relazione vertice-triangolo for ( int i = 0 ; i < 3 ; ++ i) m_vVert[nIdV[i]].nIdTria = nT ; } } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::AdjustTopologyFromZMap( void) { // cancello tutti i vertici che puntano a triangoli cancellati bool bPack = false ; for ( int i = 0 ; i < GetVertexSize() ; ++ i) { if ( m_vVert[i].nIdTria == SVT_NULL) { m_vVert[i].nIdTria = SVT_DEL ; bPack = true ; } } if ( bPack) PackVertices() ; m_nStatus = SurfTriMesh::OK ; // calcolo le adiacenze if ( ! AdjustAdjacencies()) return false ; // verifico l'orientamento ( TODO -- Da migliorare...) // Ora per funzionare è necessario che il ciclo sui triangoli parta da un triangolo orientato // correttamente e che i triangoli invertiti siano "circondati" da triangoli tutti orientati correttamente if ( ! AdjustOrientations()) return false ; // calcolo le facce if ( ! VerifyFaceting()) return false ; // rimozione delle TJunction bool bModified = false ; if ( ! RemoveTJunctions( bModified, SQ_EPS_SMALL)) return false ; if ( bModified) { if ( ! AdjustVertices() || ! DoCompacting()) return false ; } else TestSealing() ; // semplifico le facce ( anche più piccola) if ( ! SimplifyFacets( MAX_EDGE_LEN_STD, false, 5. * EPS_SMALL)) LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::AdjustTopologyFromZMap") return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriaFromFace( int nF, int& nT) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return false ; // l'indice della faccia deve essere nei limiti if ( nF < 0 || nF >= int( m_vFacet.size())) return false ; // recupero la normale di un triangolo della faccetta nT = m_vFacet[nF] ; return true ; }