//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : SurfTriMeshUtilities.cpp Data : 25.10.21 Versione : // Contenuto : Implementazione della classe 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 ; //---------------------------------------------------------------------------- bool SurfTriMesh::SimplifyFacets( double dMaxEdgeLen) { // Se la lunghezza massima del lato del triangolo sul bordo della faccia è nulla, ho finito. if ( dMaxEdgeLen < EPS_SMALL) return true ; // Le superfici devono essere valide. if ( ! IsValid()) return false ; // Verifico la definizione delle facce. VerifyFaceting() ; // Ciclo sulle facce delle mesh. unordered_map FacetMap ; int nFacetNum = GetFacetCount() ; for ( int nF = 0 ; nF < nFacetNum ; ++ nF) { bool bFacetToRetriangulate = false ; // Recupero i loop della faccia. POLYLINEVECTOR LoopVec ; GetFacetLoops( nF, LoopVec) ; // Recupero le adiacenze della faccia. INTMATRIX vAdj ; GetFacetAdjacencies( nF, vAdj) ; // Ciclo sui loop della faccia. for ( int nL = 0 ; nL < int( LoopVec.size()) ; ++ nL) { // Lista dei punti del loop. PNTULIST& PointList = LoopVec[nL].GetUPointList() ; // Mi assicuro che il punto finale non sia all'interno di un segmento. auto itFirst = PointList.begin() ; int nNewStartPos = 0 ; for ( auto it = itFirst ; it != PointList.end() ; ++ it, ++ nNewStartPos) { if ( itFirst->second != it->second) { //ChangePolyLineStart( it->first, LoopVec[nL]) ; { // cancello ultimo punto ( coincide con primo) PointList.pop_back() ; // sposto la metà iniziale dei punti alla fine for ( int i = 0 ; i < nNewStartPos ; ++ i) PointList.splice( PointList.end(), PointList, PointList.begin()) ; // aggiungo punto finale come copia dell'iniziale PointList.push_back( PointList.front()) ; } break ; } else if ( itFirst->second == - 1 && it != itFirst) { // Valuto se i punti compresi fra gli estremi sono allinati. bool bAreAlligned = true ; auto itNextToLast = itFirst ; ++ itNextToLast ; for ( auto itInn = itNextToLast ; itInn != it && bAreAlligned ; ++ itInn) { DistPointLine PointLineDistCalc( itInn->first, itFirst->first, it->first, false) ; double dDist ; PointLineDistCalc.GetDist( dDist) ; bAreAlligned = bAreAlligned && dDist < EPS_SMALL ; } // I punti sono allineati. if ( bAreAlligned) { // Valuto se includendo anche il punto successivo non ci sarebbe più allineamento, // ovvero avrei trovato un insieme massimale di punti allineati. auto itNextToCurr = it ; ++ itNextToCurr ; // Se il punto corrente è l'ultimo, ho trovato un insieme massimale di punti allineati. if ( itNextToCurr != PointList.end()) { for ( auto itInn = itNextToLast ; itInn != itNextToCurr && bAreAlligned ; ++ itInn) { DistPointLine PointLineDistCalc( itInn->first, itFirst->first, itNextToCurr->first, false) ; double dDist ; PointLineDistCalc.GetDist( dDist) ; bAreAlligned = bAreAlligned && dDist < EPS_SMALL ; } } // Se ho trovato un insieme massimale di punti allineati li ridistribuisco, // altrimenti procedo per includere il punto successivo nell'insieme. if ( ! bAreAlligned || itNextToCurr == PointList.end()) { //ChangePolyLineStart( it->first, LoopVec[nL]) ; { // cancello ultimo punto ( coincide con primo) PointList.pop_back() ; // sposto la metà iniziale dei punti alla fine for ( int i = 0 ; i < nNewStartPos ; ++ i) PointList.splice( PointList.end(), PointList, PointList.begin()) ; // aggiungo punto finale come copia dell'iniziale PointList.push_back( PointList.front()) ; } break ; } } } } // Ciclo sui punti del loop. auto itLast = PointList.begin() ; for ( auto it = itLast ; it != PointList.end() ; ++ it) { // Dal punto corrente spicca un segmento adiacente a una nuova faccia. if ( itLast->second != it->second) { // Elimino punti interni. auto itNextToLast = itLast ; ++ itNextToLast ; for ( auto itInn = itNextToLast ; itInn != it ; ) { itInn = PointList.erase( itInn) ; bFacetToRetriangulate = true ; } // Versore direzione del segmento e sua lunghezza. Vector3d vtSeg = it->first - itLast->first ; double dSegLen = vtSeg.Len() ; vtSeg /= dSegLen ; // Calcolo numero dei punti da inserire. double dRatio = dSegLen / dMaxEdgeLen ; int nStepNum = ( dRatio < 1 + EPS_SMALL ? int( dRatio) : int( dRatio) + 1) ; // Aggiungo i nuovi punti. double dMyLen = dSegLen / nStepNum ; auto itAdd = it ; for ( int nP = 1 ; nP < nStepNum ; ++ nP) { itAdd = PointList.insert( itAdd, POINTU( it->first - nP * dMyLen * vtSeg, itLast->second)) ; bFacetToRetriangulate = true ; } itLast = it ; } // Possono essere due segmenti liberi non allineati. else if ( itLast->second == - 1 && it != itLast) { // Valuto se i punti compresi fra gli estremi sono allinati. bool bAreAlligned = true ; auto itNextToLast = itLast ; ++ itNextToLast ; for ( auto itInn = itNextToLast ; itInn != it && bAreAlligned ; ++ itInn) { DistPointLine PointLineDistCalc( itInn->first, itLast->first, it->first, false) ; double dDist ; PointLineDistCalc.GetDist( dDist) ; bAreAlligned = bAreAlligned && dDist < EPS_SMALL ; } // I punti sono allineati. if ( bAreAlligned) { // Valuto se includendo anche il punto successivo non ci sarebbe più allineamento, // ovvero avrei trovato un insieme massimale di punti allineati. auto itNextToCurr = it ; ++ itNextToCurr ; // Se il punto corrente è l'ultimo, ho trovato un insieme massimale di punti allineati. if ( itNextToCurr != PointList.end()) { for ( auto itInn = itNextToLast ; itInn != itNextToCurr && bAreAlligned ; ++ itInn) { DistPointLine PointLineDistCalc( itInn->first, itLast->first, itNextToCurr->first, false) ; double dDist ; PointLineDistCalc.GetDist( dDist) ; bAreAlligned = bAreAlligned && dDist < EPS_SMALL ; } } // Se ho trovato un insieme massimale di punti allineati li ridistribuisco, // altrimenti procedo per includere il punto successivo nell'insieme. if ( ! bAreAlligned || itNextToCurr == PointList.end()) { for ( auto itInn = itNextToLast ; itInn != it ; ) { itInn = PointList.erase( itInn) ; bFacetToRetriangulate = true ; } // Versore direzione del segmento e sua lunghezza. Vector3d vtSeg = it->first - itLast->first ; double dSegLen = vtSeg.Len() ; vtSeg /= dSegLen ; // Calcolo numero dei punti da inserire. double dRatio = dSegLen / dMaxEdgeLen ; int nStepNum = ( dRatio < 1 + EPS_SMALL ? int( dRatio) : int( dRatio) + 1) ; // Aggiungo i nuovi punti. double dMyLen = dSegLen / nStepNum ; auto itAdd = it ; for ( int nP = 1 ; nP < nStepNum ; ++ nP) { itAdd = PointList.insert( itAdd, POINTU( it->first - nP * dMyLen * vtSeg, itLast->second)) ; bFacetToRetriangulate = true ; } itLast = it ; } } } } } if ( bFacetToRetriangulate) { FacetMap.emplace( nF, LoopVec) ; } } // Ciclo sulle facce da ritriangolare per eliminare i triangoli. for ( auto itF = FacetMap.begin() ; itF != FacetMap.end() ; ++ itF) { // Cancello i triangoli della faccia. INTVECTOR vFacetTria ; GetAllTriaInFacet( itF->first, vFacetTria) ; for ( int& nT : vFacetTria) RemoveTriangle( nT) ; } // Ritriangolo le facce. for ( auto itF = FacetMap.begin() ; itF != FacetMap.end() ; ++ itF) { // Ritriangolo la faccia. PNTVECTOR vPt ; INTVECTOR vTr ; if ( Triangulate().Make( itF->second, vPt, vTr)) { // Inserisco i nuovi triangoli. for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; int nNewId[3] = { AddVertex(vPt[nNewTriaVertId[0]]), AddVertex(vPt[nNewTriaVertId[1]]), AddVertex(vPt[nNewTriaVertId[2]]) } ; // Il colore passato nel secondo parametro è definito perché il vettore delle facce ha ancora salvato l'indice di un suo triangolo e // il triangolo è cancellato semplicente assegnadno la costante apposita VT_DEL (-2) a nIdVert[0]; ma il suo colore resta definito. int nNewTriaNum = AddTriangle( nNewId, m_vTria[m_vFacet[itF->first]].nTFlag) ; } } } return AdjustVertices() && DoCompacting() ; }