aa82c82ddc
- corretta SurfTriMesh::SimplifyFacets.
251 lines
9.4 KiB
C++
251 lines
9.4 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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 <unordered_map>
|
|
|
|
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()) ;
|
|
}
|