//---------------------------------------------------------------------------- // EgalTech 2015-2023 //---------------------------------------------------------------------------- // File : SurfTriMeshFaceting.cpp Data : 09.12.23 Versione : 2.5l2 // Contenuto : Implementazione della classe Superfici TriMesh. // // // // Modifiche : 26.03.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "SurfTriMesh.h" #include "GeoConst.h" #include "PolygonPlane.h" #include "CurveLine.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include #include #include using namespace std ; //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyFaceting( void) const { if ( m_bFaceted) return true ; return (const_cast(this))->UpdateFaceting() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::UpdateFaceting( void) { // salvo vecchio vettore facce INTVECTOR vOldFacet = m_vFacet ; // reset faceting m_bFaceted = false ; m_vFacet.clear() ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) m_vTria[i].nIdFacet = SVT_NULL ; m_bFacEdged = false ; // indice faccia corrente int nFacet = -1 ; // ricostruisco le sfaccettature come definite in precedenza (dove possibile) bool bOk = true ; for ( int j = 0 ; j < int( vOldFacet.size()) ; ++ j) { int i = vOldFacet[j] ; // salto triangoli inesistenti o già assegnati if ( i >= int( m_vTria.size()) || m_vTria[i].nIdVert[0] == SVT_DEL || m_vTria[i].nIdFacet != SVT_NULL) continue ; // assegno indice di faccia al triangolo m_vTria[i].nIdFacet = ++ nFacet ; m_vFacet.push_back( i) ; // aggiorno faccia if ( ! UpdateOneFace( nFacet, i)) bOk = false ; } // ricostruisco le altre sfaccettature 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].nIdFacet != SVT_NULL) continue ; // assegno indice di faccia al triangolo m_vTria[i].nIdFacet = ++ nFacet ; m_vFacet.push_back( i) ; // aggiorno faccia if ( ! UpdateOneFace( nFacet, i)) bOk = false ; } // se ci sono stati problemi, salvo nel log if ( ! bOk) LOG_ERROR( GetEGkLogger(), "SurfTM : UpdateFaceting error") // calcolo facce piane effettuato m_bFaceted = bOk ; return bOk ; } //---------------------------------------------------------------------------- bool SurfTriMesh::UpdateOneFace( int nFacet, int nT) { // piano del triangolo Plane3d plPlane ; if ( ! plPlane.Set( m_vVert[m_vTria[nT].nIdVert[0]].ptP, m_vTria[nT].vtN)) { LOG_ERROR( GetEGkLogger(), "SurfTM : UpdateFaceting error in triangle data") return false ; } // set di triangoli da aggiornare set stTria ; stTria.insert( nT) ; // finchè set non vuoto bool bOk = true ; 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].nIdFacet == SVT_NULL) { if ( ! UpdateTriaFaceting( nT, nFacet, plPlane, nAdjT)) bOk = false ; if ( m_vTria[nAdjT].nIdFacet == nFacet) stTria.insert( nAdjT) ; } } } return bOk ; } //---------------------------------------------------------------------------- bool SurfTriMesh::UpdateTriaFaceting( int nRefT, int nFacet, const Plane3d& plPlane, int nT) { // verifica scostamento della normale da quella del piano if ( plPlane.GetVersN() * m_vTria[nT].vtN < m_dCosBndAng) return true ; // verifica scostamento del vertice opposto al lato in comune dal piano int nV = SVT_NULL ; for ( int i = 0 ; i < 3 ; ++i) { if ( m_vTria[nT].nIdAdjac[i] == nRefT) { nV = Prev( i) ; break ; } } if ( nV == SVT_NULL) return true ; double dTol = max( min( m_dLinTol, 20 * EPS_SMALL), 2 * EPS_SMALL) ; if ( ! PointInPlaneEpsilon( m_vVert[m_vTria[nT].nIdVert[nV]].ptP, plPlane, dTol)) return true ; // il triangolo fa parte della faccia m_vTria[nT].nIdFacet = nFacet ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFacetCount( void) const { // la superficie deve essere validata if ( m_nStatus != OK) return 0 ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return 0 ; // restituisco il numero return int( m_vFacet.size()) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SetFacet( int nInd, int nT) { // recupero la dimensione originale int nPrevSize = int( m_vFacet.size()) ; // determino la dimensione necessaria int nNewSize = max( nInd + 1, nPrevSize) ; // se necessaria dimensione maggiore if ( nNewSize > nPrevSize) { // espando vettore try { m_vFacet.resize( nNewSize) ; } catch (...) { return false ; } // inizializzo a cancellate le eventuali facce intermedie if ( ( nNewSize - nPrevSize) > 1) { for ( int i = nPrevSize ; i < nNewSize ; ++ i) m_vFacet[i] = SVT_DEL ; } } // inserisco la faccia m_vFacet[nInd] = nT ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetFacetFromTria( int nT) const { // la superficie deve essere validata if ( m_nStatus != OK) return SVT_NULL ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return SVT_NULL ; // l'indice del triangolo deve essere nei limiti if ( nT < 0 || nT >= int( m_vTria.size()) || m_vTria[nT].nIdVert[0] == SVT_DEL) return SVT_NULL ; // restituisco l'indice di faccia return m_vTria[nT].nIdFacet ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetAllTriaInFacet( int nF, INTVECTOR& vT) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return false ; // l'indice della faccia deve essere nei limiti if ( nF < 0 || nF >= int( m_vFacet.size())) return false ; // recupero l'indice del primo triangolo della faccia int nT = m_vFacet[nF] ; // incremento time stamp ++ m_nTimeStamp ; // recupero i triangoli della faccia vT.clear() ; vT.reserve( 10) ; vT.push_back( nT) ; m_vTria[nT].nTemp = m_nTimeStamp ; return VerifyAdjacTriaFacet( vT) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyAdjacTriaFacet( INTVECTOR& vT) const { INTVECTOR vStack{ vT.back()} ; while ( ! vStack.empty()) { array vNew{ SVT_NULL, SVT_NULL, SVT_NULL} ; for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[vStack.back()].nIdAdjac[j] ; if ( nAdjT != SVT_NULL && m_vTria[nAdjT].nTemp != m_nTimeStamp && m_vTria[nAdjT].nIdFacet == m_vTria[vStack.back()].nIdFacet) { vT.push_back( nAdjT) ; vNew[j] = nAdjT ; m_vTria[nAdjT].nTemp = m_nTimeStamp ; } } vStack.pop_back() ; for ( int j = 0 ; j < 3 ; ++ j) { if ( vNew[j] != SVT_NULL) vStack.push_back( vNew[j]) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetAllVertInFacet( int nF, INTVECTOR& vVert) const { // recupero tutti i triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // ne ricavo i vertici vVert.clear() ; INTUNORDSET usTempVert ; for ( int nT : vTria) { for ( int i = 0 ; i < 3 ; ++ i) { int nV = m_vTria[nT].nIdVert[i] ; if ( usTempVert.find( nV) == usTempVert.end()) { usTempVert.insert( nV) ; vVert.push_back( nV) ; } } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetNearestEndPoint( int nF, const Point3d& ptNear, Point3d& ptEnd, Vector3d& vtN) const { // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // ciclo sui triangoli e sui loro lati di bordo bool bFound = false ; double dMinSqDist = SQ_INFINITO ; for ( int i = 0 ; i < int( vTria.size()) ; ++i) { int nT = vTria[i] ; for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdFacet != nF) { Point3d ptTest = m_vVert[m_vTria[nT].nIdVert[j]].ptP ; double dSqDist = SqDist( ptTest, ptNear) ; if ( dSqDist < dMinSqDist) { // salvo i dati del punto dMinSqDist = dSqDist ; ptEnd = ptTest ; bFound = true ; // recupero la normale della faccia (non quella mediata del vertice) vtN = m_vTria[nT].vtN ; } } } } return bFound ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetNearestMidPoint( int nF, const Point3d& ptNear, Point3d& ptMid, Vector3d& vtN) const { // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // ciclo sui triangoli e sui loro lati di bordo bool bFound = false ; double dMinSqDist = SQ_INFINITO ; for ( int i = 0 ; i < int( vTria.size()) ; ++i) { int nT = vTria[i] ; for ( int j = 0 ; j < 3 ; ++ j) { int k = Next( j) ; int nAdjT = m_vTria[nT].nIdAdjac[j] ; if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdFacet != nF) { Point3d ptTest = Media( m_vVert[m_vTria[nT].nIdVert[j]].ptP, m_vVert[m_vTria[nT].nIdVert[k]].ptP, 0.5) ; double dSqDist = SqDist( ptTest, ptNear) ; if ( dSqDist < dMinSqDist) { // salvo i dati del punto dMinSqDist = dSqDist ; ptMid = ptTest ; bFound = true ; // recupero la normale della faccia (non quella mediata del vertice) vtN = m_vTria[nT].vtN ; } } } } return bFound ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetLoops( int nF, POLYLINEVECTOR& vPL) const { // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // incremento time stamp ++ m_nTimeStamp ; // ciclo sui triangoli for ( int i = 0 ; i < int( vTria.size()) ; ++ i) { int nT = vTria[i] ; // se triangolo non ancora visitato if ( m_vTria[nT].nTemp != m_nTimeStamp) { // determino i triangoli adiacenti int nAdjT[3] ; for ( int j = 0 ; j < 3 ; ++ j) nAdjT[j] = m_vTria[nT].nIdAdjac[j] ; // determino i facet adiacenti int nAdjF[3] ; for ( int j = 0 ; j < 3 ; ++ j) nAdjF[j] = ( nAdjT[j] != SVT_NULL ? m_vTria[nAdjT[j]].nIdFacet : SVT_NULL) ; // se tutti e tre i lati sono di contorno if ( nAdjF[0] != nF && nAdjF[1] != nF && nAdjF[2] != nF) { // ho trovato un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[0], m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nAdjF[1], m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nAdjF[2], m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( nAdjF[0] != nF && nAdjF[1] != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[0], m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( nAdjF[1], m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( ! MarchAlongFacetLoop( nF, nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 1 e 2 sono di contorno else if ( nAdjF[1] != nF && nAdjF[2] != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[1], m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( nAdjF[2], m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( ! MarchAlongFacetLoop( nF, nT, 0, m_nTimeStamp, vPL.back())) return false ; } // se i due lati 2 e 0 sono di contorno else if ( nAdjF[2] != nF && nAdjF[0] != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[2], m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( nAdjF[0], m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( ! MarchAlongFacetLoop( nF, nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 0 è di contorno else if ( nAdjF[0] != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[0], m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( ! MarchAlongFacetLoop( nF, nT, 1, m_nTimeStamp, vPL.back())) return false ; } // se il lato 1 è di contorno else if ( nAdjF[1] != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[1], m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( ! MarchAlongFacetLoop( nF, nT, 2, m_nTimeStamp, vPL.back())) return false ; } // se il lato 2 è di contorno else if ( nAdjF[2] != nF) { // ho trovato l'inizio di un loop vPL.emplace_back() ; vPL.back().AddUPoint( nAdjF[2], m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; vPL.back().AddUPoint( SVT_NULL, 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 ( ! MarchAlongFacetLoop( nF, nT, 0, m_nTimeStamp, vPL.back())) return false ; } // altrimenti non c'è contorno else { // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; } } } // normale di riferimento della faccia Vector3d vtN = m_vTria[vTria[0]].vtN ; // in prima posizione ci deve essere il loop esterno bool bOutFirst = false ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { Plane3d plPlane ; double dArea ; if ( ! vPL[i].IsClosedAndFlat( plPlane, dArea, 100 * EPS_SMALL)) return false ; // se loop esterno if ( vtN * plPlane.GetVersN() > 0) { // se non c'è ancora loop esterno in prima posizione if ( ! bOutFirst) { // lo sposto in prima posizione if ( i != 0) swap( vPL[0], vPL[i]) ; bOutFirst = true ; } // altrimenti errore else return false ; } } return bOutFirst ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MarchAlongFacetLoop( int nF, int nT, int nV, int nTimeStamp, PolyLine& PL) const { // mi muovo lungo il loop, un triangolo alla volta bool bEnd = false ; while ( ! bEnd) { if ( ! MarchOneFacetTria( nF, nT, nV, nTimeStamp, PL, bEnd)) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MarchOneFacetTria( int nF, int& nT, int& nV, int nTimeStamp, PolyLine& PL, bool& bEnd) const { // verifico esistenza triangolo adiacente, sul lato dopo il vertice if ( m_vTria[nT].nIdAdjac[nV] == SVT_NULL) return false ; int nAdjT = m_vTria[nT].nIdAdjac[nV] ; // verifico appartenga alla stessa faccia if ( m_vTria[nAdjT].nIdFacet != nF) return false ; // recupero il suo lato di adiacenza (e verifico non abbia più adiacenze con il triangolo di partenza) int nAdjS = SVT_NULL ; for ( int i = 0 ; i < 3 ; ++ i) { if ( m_vTria[nAdjT].nIdAdjac[i] == nT) { if ( nAdjS == SVT_NULL) nAdjS = i ; else return false ; } } 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] ; int nNextF = ( nNextT != SVT_NULL ? m_vTria[nNextT].nIdFacet : SVT_NULL) ; if ( nNextF != nF) { // se già recuperato if ( m_vTria[nAdjT].nTemp == nTimeStamp) { bEnd = true ; return true ; } // dichiaro triangolo analizzato m_vTria[nAdjT].nTemp = nTimeStamp ; // modifico l'ultima adiacenza e aggiungo il lato al loop PL.ModifyLastParam( nNextF) ; nAdjV = Next( nAdjV) ; PL.AddUPoint( SVT_NULL, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ; // verifico anche il successivo nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ; nNextF = ( nNextT != SVT_NULL ? m_vTria[nNextT].nIdFacet : SVT_NULL) ; if ( nNextF != nF) { // modifico l'ultima adiacenza e aggiungo il lato al loop PL.ModifyLastParam( nNextF) ; nAdjV = Next( nAdjV) ; PL.AddUPoint( SVT_NULL, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ; } } // devo passare al triangolo adiacente nT = nAdjT ; nV = nAdjV ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetAdjacencies( int nF, INTMATRIX& vAdj) const { // recupero i loop di contorno POLYLINEVECTOR vPL ; if ( ! GetFacetLoops( nF, vPL)) return false ; // ne ricavo le adiacenze vAdj.resize( vPL.size()) ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { vAdj[i].reserve( vPL[i].GetPointNbr()) ; double dU ; for ( bool bFound = vPL[i].GetFirstU( dU, true) ; bFound ; bFound = vPL[i].GetNextU( dU, true)) vAdj[i].emplace_back( int( round( dU))) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetsContact( int nF1, int nF2, bool& bAdjac, Point3d& ptP1, Point3d& ptP2, double& dAng) const { // recupero i loop di contorno della prima faccia POLYLINEVECTOR vPL ; if ( ! GetFacetLoops( nF1, vPL)) return false ; // verifico esistenza seconda faccia if ( nF2 < 0 || nF2 >= int( m_vFacet.size())) return false ; // verifico se c'è un contatto con la seconda faccia e recupero i punti estremi della eventuale linea di contatto bAdjac = false ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { double dUs, dUe ; Point3d ptPs, ptPe ; for ( bool bFound = vPL[i].GetFirstULine( &dUs, &ptPs, &dUe, &ptPe) ; bFound ; bFound = vPL[i].GetNextULine( &dUs, &ptPs, &dUe, &ptPe)) { if ( int( round( dUs)) == nF2) { if ( ! bAdjac) { bAdjac = true ; ptP1 = ptPs ; ptP2 = ptPe ; } else { // parametri del segmento già definito Vector3d vtT ; double dLen ; DirDist( ptP1, ptP2, vtT, dLen) ; // punto start rispetto al segmento double dPs = ( ptPs - ptP1) * vtT ; if ( dPs < - EPS_SMALL) ptP1 = ptPs ; else if ( dPs > dLen + EPS_SMALL) ptP2 = ptPs ; // punto end rispetto al segmento double dPe = ( ptPe - ptP1) * vtT ; if ( dPe < - EPS_SMALL) ptP1 = ptPe ; else if ( dPe > dLen + EPS_SMALL) ptP2 = ptPe ; } } } } // se facce in contatto, calcolo l'angolo che formano ( 0 = allineate, > 0 convesse, < 0 concave) if ( bAdjac) { Vector3d vtN1 = m_vTria[m_vFacet[nF1]].vtN ; Vector3d vtN2 = m_vTria[m_vFacet[nF2]].vtN ; Vector3d vtCm = ptP2 - ptP1 ; bool bDet ; if ( ! vtN1.GetRotation( vtN2, vtCm, dAng, bDet) || ! bDet) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetCenter( int nF, Point3d& ptCen, Vector3d& vtN) const { // recupero i loop della faccia POLYLINEVECTOR vPL ; if ( ! GetFacetLoops( nF, vPL) || vPL.size() == 0) return false ; // calcolo il centro del loop esterno (è il primo) PolygonPlane PolyPlane ; Point3d ptP ; for ( bool bFound = vPL[0].GetFirstPoint( ptP) ; bFound ; bFound = vPL[0].GetNextPoint( ptP)) PolyPlane.AddPoint( ptP) ; if ( ! PolyPlane.GetCentroid( ptCen)) return false ; // recupero la normale di un triangolo della faccetta vtN = m_vTria[m_vFacet[nF]].vtN ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetNormal( int nF, Vector3d& vtN) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato sfaccettatura if ( ! VerifyFaceting()) return false ; // l'indice della faccia deve essere nei limiti if ( nF < 0 || nF >= int( m_vFacet.size())) return false ; // recupero la normale di un triangolo della faccetta vtN = m_vTria[m_vFacet[nF]].vtN ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetArea( int nF, double& dArea) const { // recupero i loop della faccia POLYLINEVECTOR vPL ; if ( ! GetFacetLoops( nF, vPL) || vPL.size() == 0) return false ; // sommo le aree dei diversi loop (quelli interni hanno area negativa) dArea = 0 ; for ( size_t i = 0 ; i < vPL.size() ; ++ i) { PolygonPlane PolyPlane ; Point3d ptP ; for ( bool bFound = vPL[i].GetFirstPoint( ptP) ; bFound ; bFound = vPL[i].GetNextPoint( ptP)) PolyPlane.AddPoint( ptP) ; double dLoopArea ; if ( ! PolyPlane.GetArea( dLoopArea)) return false ; if ( i == 0) dArea += dLoopArea ; else dArea -= dLoopArea ; } return true ; } //---------------------------------------------------------------------------- SurfTriMesh* SurfTriMesh::CloneFacet( int nF) const { if ( ! IsValid()) return nullptr ; // alloco la nuova superficie PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM)) return nullptr ; // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return nullptr ; // preparo lo spazio nella nuova superficie int nTria = int( vTria.size()) ; if ( ! pSTM->Init( nTria + 2, nTria, 1)) return nullptr ; // tabella hash per indici vertici (vecchi e nuovi indici) typedef unordered_map VVMAP ; VVMAP PntMap( nTria + 2) ; // inserisco i triangoli nella nuova superficie for ( int i = 0 ; i < nTria ; ++ i) { // indice del triangolo int nT = vTria[i] ; // ciclo sui tre vertici int nIdV[3] ; for ( int j = 0 ; j < 3 ; ++ j) { // verifico se vertice già presente const auto it = PntMap.find( m_vTria[nT].nIdVert[j]) ; if ( it == PntMap.end()) { // aggiungo il vertice if ( ( nIdV[j] = pSTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[j]].ptP)) == SVT_NULL) return nullptr ; PntMap.insert( VVMAP::value_type( m_vTria[nT].nIdVert[j], nIdV[j])) ; } else nIdV[j] = it->second ; } // inserisco il triangolo if ( pSTM->AddTriangle( nIdV) == SVT_NULL) return nullptr ; } // sistemazioni finali della superficie if ( ! pSTM->AdjustTopology()) return nullptr ; // restituisco la superficie return Release( pSTM) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::RemoveFacet( int nF) { // verifico esistenza delle due facce if ( ! IsValid() || nF < 0 || nF >= GetFacetCount()) return false ; // recupero l'elenco dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // Rimuovo i triangoli della faccia for ( int i : vTria) RemoveTriangle( i) ; // aggiorno le strutture di base dell'oggetto if ( ! DoCompacting()) return false ; // dichiaro necessità ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SwapFacets( int nF1, int nF2) { // verifico esistenza delle due facce if ( ! IsValid() || nF1 < 0 || nF1 >= GetFacetCount() || nF2 < 0 || nF2 >= GetFacetCount()) return false ; // recupero gli indici dei triangoli della prima e seconda faccia INTVECTOR vTria1 ; if ( ! GetAllTriaInFacet( nF1, vTria1)) return false ; INTVECTOR vTria2 ; if ( ! GetAllTriaInFacet( nF2, vTria2)) return false ; // scambio gli indici di faccia dei triangoli for ( int i = 0 ; i < int( vTria1.size()) ; ++ i) m_vTria[vTria1[i]].nIdFacet = nF2 ; for ( int i = 0 ; i < int( vTria2.size()) ; ++ i) m_vTria[vTria2[i]].nIdFacet = nF1 ; // scambio i riferimenti al primo triangolo delle facce swap( m_vFacet[nF1], m_vFacet[nF2]) ; return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetLocalBBox( int nF, BBox3d& b3Loc, int nFlag) const { // verifico esistenza della faccia if ( nF < 0 || nF >= GetFacetCount()) return false ; // recupero gli indici dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // assegno il box in locale b3Loc.Reset() ; for ( int i = 0 ; i < int( vTria.size()) ; ++ i) { // indice del triangolo int nT = vTria[i] ; // ciclo sui tre vertici for ( int j = 0 ; j < 3 ; ++ j) b3Loc.Add( m_vVert[m_vTria[nT].nIdVert[j]].ptP) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetFacetBBox( int nF, const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const { // verifico esistenza della faccia if ( nF < 0 || nF >= GetFacetCount()) return false ; // recupero gli indici dei triangoli della faccia INTVECTOR vTria ; if ( ! GetAllTriaInFacet( nF, vTria)) return false ; // assegno il box nel riferimento b3Ref.Reset() ; for ( int i = 0 ; i < int( vTria.size()) ; ++ i) { // indice del triangolo int nT = vTria[i] ; // ciclo sui tre vertici for ( int j = 0 ; j < 3 ; ++ j) { Point3d ptTemp = m_vVert[m_vTria[nT].nIdVert[j]].ptP ; ptTemp.ToGlob( frRef) ; b3Ref.Add( ptTemp) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::VerifyFacetEdging( void) const { if ( m_bFacEdged) return true ; return (const_cast(this))->UpdateFacetEdging() ; } //---------------------------------------------------------------------------- bool SurfTriMesh::UpdateFacetEdging( void) { // reset degli Edge m_bFacEdged = false ; m_vFacEdge.clear() ; // verifico validità sfaccettatura if ( ! VerifyFaceting()) return false ; // reset flag temporaneo di edge dei triangoli for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { m_vTria[i].nETempFlag[0] = 0 ; m_vTria[i].nETempFlag[1] = 0 ; m_vTria[i].nETempFlag[2] = 0 ; } // ricostruisco gli edge delle facce for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) { StmTria& Tria = m_vTria[nT] ; // salto triangoli cancellati if ( Tria.nIdVert[0] == SVT_DEL) continue ; // ciclo sui lati del triangolo for ( int nE = 0 ; nE < 3 ; ++ nE) { int nE2 = ( nE + 1) % 3 ; int nTAdj = Tria.nIdAdjac[nE] ; if ( Tria.nETempFlag[nE] == 0) { if ( nTAdj == SVT_NULL) { m_vFacEdge.emplace_back( Tria.nIdVert[nE], Tria.nIdVert[nE2], Tria.nIdFacet, SVT_NULL, 180.) ; } else if ( Tria.nIdFacet != m_vTria[nTAdj].nIdFacet) { // calcolo l'angolo tra le facce ( 0 = allineate, > 0 convesse, < 0 concave) Vector3d vtN1 = Tria.vtN ; Vector3d vtN2 = m_vTria[nTAdj].vtN ; Vector3d vtCm = m_vVert[Tria.nIdVert[nE2]].ptP - m_vVert[Tria.nIdVert[nE]].ptP ; double dAng ; bool bDet ; if ( ! vtN1.GetRotation( vtN2, vtCm, dAng, bDet) || ! bDet) dAng = 0 ; // inserisco in lista m_vFacEdge.emplace_back( Tria.nIdVert[nE], Tria.nIdVert[nE2], Tria.nIdFacet, m_vTria[nTAdj].nIdFacet, dAng) ; // marco i due semi edge int nEAdj = 0 ; if ( m_vTria[nTAdj].nIdAdjac[1] == nT) nEAdj = 1 ; else if ( m_vTria[nTAdj].nIdAdjac[2] == nT) nEAdj = 2 ; Tria.nETempFlag[nE] = 1 ; m_vTria[nTAdj].nETempFlag[nEAdj] = 1 ; } } } } // calcolo bordo delle facce piane effettuato m_bFacEdged = true ; return true ; } //---------------------------------------------------------------------------- int SurfTriMesh::GetEdgeCount( void) const { // la superficie deve essere validata if ( m_nStatus != OK) return -1 ; // verifico stato bordi sfaccettatura if ( ! VerifyFacetEdging()) return -1 ; // restituisco il numero return int( m_vFacEdge.size()) ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetEdge( int nInd, int& nV1, int& nV2, int& nFl, int& nFr, double& dAng) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato bordi sfaccettatura if ( ! VerifyFacetEdging()) return false ; // verifico la validità dell'indice if ( nInd < 0 || nInd > int( m_vFacEdge.size())) return SVT_NULL ; // recupero i dati nV1 = m_vFacEdge[nInd].nIdVert[0] ; nV2 = m_vFacEdge[nInd].nIdVert[1] ; nFl = m_vFacEdge[nInd].nIdFacAdj[0] ; nFr = m_vFacEdge[nInd].nIdFacAdj[1] ; dAng = m_vFacEdge[nInd].dIntAng ; // ritorno indice edge corrente return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetEdge( int nInd, Point3d& ptP1, Point3d& ptP2, double& dAng) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato bordi sfaccettatura if ( ! VerifyFacetEdging()) return false ; // verifico la validità dell'indice if ( nInd < 0 || nInd > int( m_vFacEdge.size())) return SVT_NULL ; // recupero i dati ptP1 = m_vVert[m_vFacEdge[nInd].nIdVert[0]].ptP ; ptP2 = m_vVert[m_vFacEdge[nInd].nIdVert[1]].ptP ; dAng = m_vFacEdge[nInd].dIntAng ; // ritorno indice edge corrente return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::GetEdges( ICURVEPOVECTOR& vpCurve) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico stato bordi sfaccettatura if ( ! VerifyFacetEdging()) return false ; // ciclo sugli edge, creo le curve e le assegno al parametro da restituire for ( int nE = 0 ; nE < int( m_vFacEdge.size()) ; ++ nE) { Point3d ptS, ptE ; if ( ! GetVertex( m_vFacEdge[nE].nIdVert[0], ptS) || ! GetVertex( m_vFacEdge[nE].nIdVert[1], ptE)) return false ; PtrOwner pLine( CreateBasicCurveLine()) ; if ( IsNull( pLine) || ! pLine->Set( ptS, ptE)) continue ; pLine->SetTempParam( m_vFacEdge[nE].dIntAng) ; vpCurve.emplace_back( Release( pLine)) ; } return true ; }