//---------------------------------------------------------------------------- // EgalTech 2021-2021 //---------------------------------------------------------------------------- // File : SurfTriMeshUtilities.cpp Data : 01.11.21 Versione : 2.3k1 // Contenuto : Implementazione funzioni di utilità di Superfici TriMesh. // // // // Modifiche : 25.10.21 LM Creazione modulo. // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "Triangulate.h" #include "SurfTriMesh.h" #include "DistPointLine.h" #include using namespace std ; //---------------------------------------------------------------------------- static bool IsVertex( PNTULIST& PointList, PNTULIST::const_iterator itCurr) { // recupero il punto precedente PNTULIST::const_iterator itPrev ; if ( itCurr == PointList.begin()) itPrev = prev( PointList.end(), 2) ; else itPrev = prev( itCurr) ; // recupero il punto successivo auto itNext = next( itCurr) ; if ( itNext == PointList.end()) itNext = next( PointList.begin()) ; // se cambia faccia adiacente tra prima e dopo, va bene if ( itPrev->second != itCurr->second) return true ; // se lati aperti e cambia direzione tra prima e dopo, va bene if ( itPrev->second == -1) { DistPointLine PointLineDistCalc( itCurr->first, itPrev->first, itNext->first) ; double dDist ; if ( PointLineDistCalc.GetDist( dDist) && dDist > EPS_SMALL) return true ; } // altrimenti non va bene return false ; } //---------------------------------------------------------------------------- static bool ChooseGoodStartPoint( PNTULIST& PointList) { // se il punto iniziale è un vertice, non devo fare alcunché if ( IsVertex( PointList, PointList.begin())) return true ; // altrimenti cerco il vertice più vicino for ( auto it = next( PointList.begin()) ; it != PointList.end() ; ++it) { if ( IsVertex( PointList, it)) { // cancello ultimo punto ( coincide con primo) PointList.pop_back() ; // sposto la parte iniziale dei punti alla fine PointList.splice( PointList.end(), PointList, PointList.begin(), it) ; // aggiungo punto finale come copia dell'iniziale PointList.push_back( PointList.front()) ; // ho finito return true ; } } return false ; } //---------------------------------------------------------------------------- static bool AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, bool& bModif) { // Ciclo sui punti del loop auto itLast = PointList.begin() ; for ( auto it = next( itLast) ; it != PointList.end() ; ++ it) { // Se dal punto corrente inizia un segmento adiacente a un'altra faccia if ( itLast->second != it->second) { // Elimino i punti interni auto itNextToLast = next( itLast) ; for ( auto itInn = itNextToLast ; itInn != it ; ) { itInn = PointList.erase( itInn) ; bModif = true ; } // Se la lunghezza del segmento supera il limite imposto double dSegLen = Dist( it->first, itLast->first) ; if ( dSegLen > dMaxEdgeLen) { // determino il numero di step double dRatio = dSegLen / dMaxEdgeLen ; int nStepCount = int( dRatio) + 1 ; // inserisco i punti auto itAdd = it ; for ( int nP = 1 ; nP < nStepCount ; ++ nP) { double dCoeff = double( nP) / nStepCount ; itAdd = PointList.insert( itAdd, POINTU( Media( itLast->first, it->first, 1 - dCoeff), itLast->second)) ; bModif = true ; } } // Nuovo punto di riferimento itLast = it ; } // Se sono due segmenti liberi non allineati else if ( itLast->second == - 1) { // Calcolo se i punti compresi fra gli estremi sono allineati bool bAreAligned = true ; auto itNextToLast = next( itLast) ; for ( auto itInn = itNextToLast ; itInn != it && bAreAligned ; ++ itInn) { DistPointLine PointLineDistCalc( itInn->first, itLast->first, it->first) ; double dDist ; if ( PointLineDistCalc.GetDist( dDist)) bAreAligned = ( dDist < EPS_SMALL) ; } // Se i punti sono allineati if ( bAreAligned) { // Verifico se il successivo punto non è più allineato auto itNextToCurr = next( it) ; if ( itNextToCurr != PointList.end()) { for ( auto itInn = itNextToLast ; itInn != itNextToCurr && bAreAligned ; ++ itInn) { DistPointLine PointLineDistCalc( itInn->first, itLast->first, itNextToCurr->first) ; double dDist ; if ( PointLineDistCalc.GetDist( dDist)) bAreAligned = ( dDist < EPS_SMALL) ; } } // Se ho trovato un insieme massimale di punti allineati li processo if ( ! bAreAligned || itNextToCurr == PointList.end()) { // Elimino i punti interni for ( auto itInn = itNextToLast ; itInn != it ; ) { itInn = PointList.erase( itInn) ; bModif = true ; } // Se la lunghezza del segmento supera il limite imposto double dSegLen = Dist( it->first, itLast->first) ; if ( dSegLen > dMaxEdgeLen) { // determino il numero di step double dRatio = dSegLen / dMaxEdgeLen ; int nStepCount = int( dRatio) + 1 ; // inserisco i punti auto itAdd = it ; for ( int nP = 1 ; nP < nStepCount ; ++ nP) { double dCoeff = double( nP) / nStepCount ; itAdd = PointList.insert( itAdd, POINTU( Media( itLast->first, it->first, 1 - dCoeff), itLast->second)) ; bModif = true ; } } // Nuovo punto di riferimento itLast = it ; } } } } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::SimplifyFacets( double dMaxEdgeLen) { // La trimesh deve essere valida if ( ! IsValid()) return false ; // Se la lunghezza massima del lato del triangolo sul bordo della faccia è nulla, non devo fare alcunché if ( dMaxEdgeLen < EPS_SMALL) return true ; // Recupero il numero delle facce (esegue anche una verifica delle stesse) int nFacetCnt = GetFacetCount() ; // Ciclo sulle facce della mesh per trovare quelle da ritriangolare unordered_map< int, pair< PNTVECTOR, INTVECTOR>> FacetMap ; for ( int nF = 0 ; nF < nFacetCnt ; ++ nF) { // Recupero i loop della faccia (il parametro indica la faccia adiacente) POLYLINEVECTOR LoopVec ; GetFacetLoops( nF, LoopVec) ; // Ciclo sui loop della faccia bool bToRetriangulate = false ; for ( int nL = 0 ; nL < int( LoopVec.size()) ; ++ nL) { // Lista dei punti del loop PNTULIST& PointList = LoopVec[nL].GetUPointList() ; // Mi assicuro che il punto iniziale/finale non sia all'interno di un possibile segmento if ( ! ChooseGoodStartPoint( PointList)) return false ; // Sistemo il loop bool bModif = false ; if ( ! AdjustLoop( PointList, dMaxEdgeLen, bModif)) return false ; if ( bModif) bToRetriangulate = true ; } // Se da ritriangolare, if ( bToRetriangulate) { // Eseguo la ritriangolazione della faccia PNTVECTOR vPt ; INTVECTOR vTr ; if ( Triangulate().Make( LoopVec, vPt, vTr)) { FacetMap.emplace( nF, make_pair( vPt, vTr)) ; } // Se non riesco a triangolare anche solo questa faccia, interrompo tutto else return false ; } } // Ciclo sulle facce da ritriangolare per eliminare i triangoli (nel contempo salvo flag colore) unordered_map< int, int> ColorMap ; for ( auto itF = FacetMap.begin() ; itF != FacetMap.end() ; ++ itF) { // Recupero i triangoli della faccia INTVECTOR vFacetTria ; GetAllTriaInFacet( itF->first, vFacetTria) ; // Salvo il colore della faccia da flag di un suo triangolo ColorMap.emplace( itF->first, m_vTria[m_vFacet[itF->first]].nTFlag) ; // Cancello i triangoli della faccia. for ( int nT : vFacetTria) RemoveTriangle( nT) ; } // Applico le nuove triangolazioni delle facce for ( auto itFac = FacetMap.begin() ; itFac != FacetMap.end() ; ++ itFac) { const PNTVECTOR& vPt = itFac->second.first ; const INTVECTOR& vTr = itFac->second.second ; // Inserisco i nuovi triangoli for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { int nNewId[3] = { AddVertex( vPt[vTr[n]]), AddVertex( vPt[vTr[n + 1]]), AddVertex( vPt[vTr[n + 2]])} ; auto itCol = ColorMap.find( itFac->first) ; int nTFlag = ( itCol != ColorMap.end() ? itCol->second : 0) ; int nNewTriaId = AddTriangle( nNewId, nTFlag) ; } } // dichiaro necessità ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; // Eseguo aggiustamenti return ( AdjustVertices() && DoCompacting()) ; }