226 lines
11 KiB
C++
226 lines
11 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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 <unordered_map>
|
|
|
|
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<int, POLYLINEVECTOR> 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) {
|
|
{
|
|
// 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()) {
|
|
{
|
|
// 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() ;
|
|
} |