diff --git a/Attribs.cpp b/Attribs.cpp index 8c697bd..06a064b 100644 --- a/Attribs.cpp +++ b/Attribs.cpp @@ -101,7 +101,7 @@ Attribs::Save( NgeWriter& ngeOut) const // modo if ( ! ngeOut.WriteUchar( m_Data[MODE], ",")) return false ; - // stato (se SEL è convertito in ON) + // stato (se SEL � convertito in ON) int nStat = (( m_Data[STATUS] > GDB_ST_ON) ? GDB_ST_ON : m_Data[STATUS]) ; if ( ! ngeOut.WriteUchar( nStat, ",")) return false ; @@ -141,7 +141,7 @@ Attribs::Load( NgeReader& ngeIn) if ( ! ngeIn.ReadUchar( ucMode, ",")) return false ; m_Data[MODE] = CLIP( ucMode, GDB_MD_STD, GDB_MD_HIDDEN) ; - // stato (se SEL è convertito in ON) + // stato (se SEL � convertito in ON) unsigned char ucStat ; if ( ! ngeIn.ReadUchar( ucStat, ",")) return false ; @@ -177,7 +177,7 @@ Attribs::Load( NgeReader& ngeIn) bool Attribs::DataFromString( const string& sParam) { - // il primo parametro è diviso in 4 parti + // il primo parametro � diviso in 4 parti STRVECTOR vsParams ; Tokenize( sParam, ",", vsParams) ; // 4 parti @@ -215,7 +215,7 @@ Attribs::SetName( const string& sName) if ( sName.empty() || ! IsValidVal( sName)) return false ; - // può essere solo la prima stringa + // pu� essere solo la prima stringa STRLIST::iterator iIter ; if ( ( iIter = m_slInfo.begin()) != m_slInfo.end() && FindKey( *iIter, NAME)) { @@ -237,7 +237,7 @@ Attribs::SetName( const string& sName) bool Attribs::GetName( string& sName) const { - // può essere solo la prima stringa + // pu� essere solo la prima stringa STRLIST::const_iterator iIter ; if ( ( iIter = m_slInfo.begin()) != m_slInfo.end() && FindKey( *iIter, NAME)) { @@ -252,7 +252,7 @@ Attribs::GetName( string& sName) const bool Attribs::ExistsName( void) const { - // può essere solo la prima stringa + // pu� essere solo la prima stringa STRLIST::const_iterator iIter ; if ( ( iIter = m_slInfo.begin()) != m_slInfo.end() && FindKey( *iIter, NAME)) @@ -265,7 +265,7 @@ Attribs::ExistsName( void) const bool Attribs::RemoveName( void) { - // può essere solo la prima stringa + // pu� essere solo la prima stringa STRLIST::const_iterator iIter ; if ( ( iIter = m_slInfo.begin()) != m_slInfo.end() && FindKey( *iIter, NAME)) { @@ -285,11 +285,11 @@ Attribs::SetInfo( const string& sKey, const string& sVal) if ( ! IsValidKey( sKey) || sVal.empty() || ! IsValidVal( sVal)) return false ; - // se è il nome + // se � il nome if ( sKey == NAME) return SetName( sVal) ; - // se esiste già una stringa con quella chiave la sostituisco + // se esiste gi� una stringa con quella chiave la sostituisco for ( auto iIter = m_slInfo.begin() ; iIter != m_slInfo.end() ; ++ iIter) { if ( FindKey( *iIter, sKey)) { *iIter = sKey + EQUAL + sVal ; diff --git a/CurveAux.cpp b/CurveAux.cpp index 4e0ba4d..52b6b10 100644 --- a/CurveAux.cpp +++ b/CurveAux.cpp @@ -510,14 +510,7 @@ CurveToArcsPerpExtrCurve( const ICurve* pCrv, double dLinTol, double dAngTolDeg) bool NurbsCurveCanonicalize( CNurbsData& cnData) { - // se periodica - if ( cnData.bPeriodic) { - // va trasformata in non-periodica (clamped) - // vedere The NurbsBook di Les Piegl e Tiller - // mancano esempi per testare - return false ; - } - // se con nodi extra + // se con nodi extra if ( cnData.bExtraKnotes) { int nKnotesNbr = int( cnData.vU.size()) ; if ( nKnotesNbr < 4) @@ -526,7 +519,176 @@ NurbsCurveCanonicalize( CNurbsData& cnData) for ( int i = 0 ; i < nKnotesNbr - 2 ; ++ i) cnData.vU[i] = cnData.vU[i+1] ; cnData.vU.resize( nKnotesNbr - 2) ; - return true ; + } + + // se periodica + if ( cnData.bPeriodic || ! cnData.bClamped) { + // va trasformata in non-periodica (clamped) + // bisogna aumentare la molteplicità dei nodi u_p-1 e u_(m-p+1) fino ad arrivare al grado della nurbs + // e poi scartare nodi e punti fuori dalla regione clamped ( al di fuori della regione u_p-1 -> u_(m-p+1)) + + // l'agoritmo per l'inserimento dei nodi l' A5.1 del libro delle Nurbs ( Piegl e Tiller), con qualche modifica + // agli indici perché uso u_p-1 e u_(m-p+1), anziché u_p e u_m-p + + // comincio ad aumentare la molteplictià del nodo u_m-p+1 + int nCP = int( cnData.vCP.size()) ; + int nU = nCP + cnData.nDeg - 1 ; + int nDeg = cnData.nDeg ; + PNTVECTOR vBC ; + vBC.resize( nDeg + 1) ; + DBLVECTOR vBW ; + vBW.resize( nDeg + 1) ; + + // trovo il nodo di cui aumentare la molteplicità e ne calcolo la molteplicità + int b = nU - nDeg - 1 +1; + int i = b ; + while ( abs( cnData.vU[b] - cnData.vU[b - 1]) < EPS_ZERO) + -- b ; + int mult = min( i - b + 1, nDeg) ; // mi aspetto che sia 1, ma comunque sarà < nDeg + // recupero i punti da modificare + if ( ! cnData.bRat) { + for ( int i = 0 ; i <= nDeg - mult ; ++ i) + vBC[i] = cnData.vCP[b - nDeg + 1 + i] ; + } + else { + for ( int i = 0 ; i <= nDeg - mult ; ++ i) { + vBC[i] = cnData.vCP[b - nDeg + 1 + i] * cnData.vW[b - nDeg + 1 + i] ; + vBW[i] = cnData.vW[b - nDeg + 1 + i] ; + } + } + + // salvo i punti inalterati + int r = nDeg - mult ; // numero di volte che dovrò inserire il nodo + cnData.vCP.resize( nCP + r) ; + for ( int p = nCP - 1 ; p > b - mult ; --p) { + cnData.vCP[r + p] = cnData.vCP[p] ; + } + if ( cnData.bRat ) { + cnData.vW.resize( nCP + r) ; + for ( int p = nCP - 1 ; p > b - mult ; --p) { + cnData.vW[r + p] = cnData.vW[p] ; + } + } + + // procedo all'inserimento + int L = 0 ; + double alpha ; + double num, den ; + if ( mult < nDeg) { + // inserisco il nodo r volte + for ( int j = 1 ; j <= r ; ++ j) { + L = b - nDeg + j ; + for ( int i = 0; i <= r - j ; ++i) { + num = (cnData.vU[b] - cnData.vU[L + i]) ; + den = ( cnData.vU[i + b + 1] - cnData.vU[L + i]) ; + alpha = (cnData.vU[b] - cnData.vU[L + i])/ ( cnData.vU[i + b + 1] - cnData.vU[L + i]) ; + vBC[i] = alpha * vBC[i +1 ] + ( 1 - alpha) * vBC[i] ; + if ( cnData.bRat) { + vBW[i] = alpha * vBW[i + 1] + ( 1 - alpha) * vBW[i] ; + } + } + cnData.vCP[L + 1] = vBC[0] ; + cnData.vCP[b + nDeg - j - mult] = vBC[r - j] ; + if ( cnData.bRat ) { + cnData.vW[L + 1] = vBW[0] ; + cnData.vW[b + nDeg - j - mult] = vBW[r-j] ; + } + } + } + + // allungo il vettore dei nodi e sposto gli ultimi nodi + cnData.vU.resize(nU + r) ; + for ( int p = nU - 1 ; p > b ; --p) + cnData.vU[p + r] = cnData.vU[p] ; + // aggiungo i nodi nuovi + for ( int p = 0 ; p < r ; ++p) + cnData.vU[b + 1 + p] = cnData.vU[b] ; + nU = nU + r ; + nCP = nCP + r ; + + // aumento la molteplicità del punto u_p-1 + b = nDeg -1; + i = b ; + while ( abs( cnData.vU[b] - cnData.vU[b - 1]) < EPS_ZERO) + -- b ; + mult = min( i - b + 1, nDeg) ; // mi aspetto che sia 1, ma comunque sarà < cnData.nDeg + // recupero i punti da modificare + if ( ! cnData.bRat) { + for ( int i = 0 ; i <= nDeg - mult ; ++ i) + vBC[i] = cnData.vCP[i] ; + } + else { + for ( int i = 0 ; i <= nDeg - mult ; ++ i) { + vBC[i] = cnData.vCP[i] * cnData.vW[i] ; + vBW[i] = cnData.vW[i] ; + } + } + + r = nDeg - mult ; + // salvo i punti inalterati + cnData.vCP.resize( nCP + r) ; + for ( int p = nCP - 1 ; p > b - mult ; --p) { + cnData.vCP[r + p] = cnData.vCP[p] ; + } + if ( cnData.bRat ) { + cnData.vW.resize( nCP + r) ; + for ( int p = nCP - 1 ; p > b - mult ; --p) { + cnData.vW[r + p] = cnData.vW[p] ; + } + } + + // procedo all'inserimento + L = 0 ; + if ( mult < nDeg) { + // inserisco il nodo r volte + for ( int j = 1 ; j <= r ; ++ j) { + L = b - nDeg + j ; + for ( int i = 0; i <= r - j ; ++i) { + alpha = (cnData.vU[b] - cnData.vU[L + i])/ ( cnData.vU[i + b + 1] - cnData.vU[L + i]) ; + vBC[i] = alpha * vBC[i + 1] + ( 1 - alpha) * vBC[i] ; + if ( cnData.bRat) { + vBW[i] = alpha * vBW[i + 1] + ( 1 - alpha) * vBW[i] ; + } + } + cnData.vCP[L + 1] = vBC[0] ; + cnData.vCP[b + nDeg - j - mult] = vBC[r - j] ; + if ( cnData.bRat ) { + cnData.vW[L + 1] = vBW[0] ; + cnData.vW[b + nDeg - j - mult] = vBW[r - j] ; + } + } + } + + // allungo il vettore dei nodi e sposto gli ultimi nodi + cnData.vU.resize(nU + r) ; + for ( int p = nU - 1 ; p > b ; --p) + cnData.vU[p+r] = cnData.vU[p] ; + // aggiungo i nodi nuovi + for ( int p = 0 ; p < r ; ++p) + cnData.vU[b + 1 + p] = cnData.vU[b] ; + nU = nU + r ; + nCP = nCP + r ; + + // rendo la curva chiusa e non periodica eliminando i primi e gli ultimi nDeg punti e nodi + cnData.bPeriodic = false ; + nCP = nCP - 2 * ( nDeg - 1); + nU = nU - 2 * ( nDeg - 1); + PNTVECTOR vCP_clamped ; + vCP_clamped.resize( nCP) ; + DBLVECTOR vU_clamped ; + vU_clamped.resize( nU) ; + for ( int i = 0 ; i < nCP ; ++i) { + if ( ! cnData.bRat) + vCP_clamped[i] = cnData.vCP[i + nDeg - 1] ; + else + vCP_clamped[i] = cnData.vCP[i + nDeg - 1] / cnData.vW[i + nDeg - 1] ; + } + cnData.vCP = vCP_clamped ; + for ( int i = 0 ; i < nU ; ++i) { + vU_clamped[i] = cnData.vU[i + nDeg - 1] ; + } + cnData.vU = vU_clamped ; + } return true ; @@ -558,7 +720,7 @@ NurbsToBezierCurve( const CNurbsData& cnData) } if ( ! bOk) return nullptr ; - + // se 1 solo intervallo, la Nurbs è già una curva di Bezier if ( nInt == 1) { // creo la curva di Bezier @@ -617,6 +779,7 @@ NurbsToBezierCurve( const CNurbsData& cnData) int b = cnData.nDeg ; bool bPrevRejected = false ; // ciclo + int n = 0 ; // debug while ( b < nU - 1) { int i = b ; while ( b < nU - 1 && abs( cnData.vU[b+1] - cnData.vU[b]) < EPS_ZERO) @@ -684,7 +847,8 @@ NurbsToBezierCurve( const CNurbsData& cnData) pCrvBez.Reset() ; bPrevRejected = true ; } - + // debug + ++n ; // inizializzazioni per la prossima curva di Bezier if ( b < nU - 1) { if ( ! cnData.bRat) { @@ -708,6 +872,30 @@ NurbsToBezierCurve( const CNurbsData& cnData) } } + // se la curva ha grado 1, manca da aggiungere l'ultimo tratto + if ( cnData.nDeg == 1 ) { + // costruisco la curva di Bezier e la inserisco nella curva composita + PtrOwner pCrvBez( CreateCurveBezier()) ; + if ( ! pCrvBez->Init( cnData.nDeg, cnData.bRat)) + return nullptr ; + if ( ! cnData.bRat) { + for ( int i = 0 ; i <= cnData.nDeg ; ++ i) { + if ( ! pCrvBez->SetControlPoint( i, vBC[i])) + return nullptr ; + } + } + else { + for ( int i = 0 ; i <= cnData.nDeg ; ++ i) { + if ( ! pCrvBez->SetControlPoint( i, vBC[i] / vBW[i], vBW[i])) + return nullptr ; + } + } + if ( ! pCrvBez->IsAPoint()) { + if ( ! pCrvCompo->AddCurve( Release( pCrvBez))) + return nullptr ; + } + } + // restituisco la curva composita return Release( pCrvCompo) ; } diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index 10459d3..d61c1f0 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -342,6 +342,7 @@ copy $(TargetPath) \EgtProg\Dll64 + @@ -402,6 +403,7 @@ copy $(TargetPath) \EgtProg\Dll64 + @@ -611,6 +613,7 @@ copy $(TargetPath) \EgtProg\Dll64 + @@ -622,6 +625,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index 9a17d76..6c34969 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -474,6 +474,9 @@ File di origine\GeoOffset + + File di origine\Base + File di origine\GeoInters @@ -489,6 +492,9 @@ File di origine\GeoProject + + File di origine\Geo + @@ -1121,6 +1127,9 @@ File di intestazione + + File di intestazione + File di intestazione @@ -1133,6 +1142,9 @@ File di intestazione\Include + + File di intestazione + diff --git a/StmFromTriangleSoup.cpp b/StmFromTriangleSoup.cpp index 08db0c7..e83191b 100644 --- a/StmFromTriangleSoup.cpp +++ b/StmFromTriangleSoup.cpp @@ -4,7 +4,7 @@ // File : StmFromTriangleSoup.cpp Data : 07.05.23 Versione : 2.5e2 // Contenuto : Implementazione della classe StmFromTriangleSoup, per creare // una superficie trimesh da un insieme di triangoli -// (può essere disordinato come STL o può essere una superficie). +// (pu� essere disordinato come STL o pu� essere una superficie). // // Modifiche : 19.05.14 DS Creazione modulo. // @@ -53,7 +53,7 @@ StmFromTriangleSoup::AddTriangle( const Triangle3d& Tria) // ciclo sui tre vertici int nIdV[3] ; for ( int i = 0 ; i < 3 ; ++ i) { - // verifico se vertice già presente + // verifico se vertice gi� presente int nId ; if ( ! m_VertGrid.Find( Tria.GetP( i), 2 * EPS_SMALL, nId)) { // aggiungo il vertice @@ -76,37 +76,38 @@ StmFromTriangleSoup::AddTriangle( const Triangle3d& Tria) //---------------------------------------------------------------------------- bool -StmFromTriangleSoup::AddTriangle( const Point3d& ptP0, const Point3d& ptP1, const Point3d& ptP2) +StmFromTriangleSoup::AddTriangle( const Point3d& ptP0, const Point3d& ptP1, const Point3d& ptP2, + const double dU0, const double dV0,const double dU1, const double dV1,const double dU2, const double dV2) { - // verifico inizializzazione + // verifico inizializzazione if ( m_pSTM == nullptr) return false ; - // ciclo sui tre vertici + // ciclo sui tre vertici int nIdV[3] ; - if ( ( nIdV[0] = AddVertex( ptP0)) == SVT_NULL) + if ( ( nIdV[0] = AddVertex( ptP0, dU0, dV0)) == SVT_NULL) return false ; - if ( ( nIdV[1] = AddVertex( ptP1)) == SVT_NULL) + if ( ( nIdV[1] = AddVertex( ptP1, dU1, dV1)) == SVT_NULL) return false ; - if ( ( nIdV[2] = AddVertex( ptP2)) == SVT_NULL) + if ( ( nIdV[2] = AddVertex( ptP2, dU2, dV2)) == SVT_NULL) return false ; - // se i vertici sono tutti diversi tra loro, inserisco il triangolo + // se i vertici sono tutti diversi tra loro, inserisco il triangolo if ( nIdV[0] != nIdV[1] && nIdV[0] != nIdV[2] && nIdV[1] != nIdV[2]) { if ( m_pSTM->AddTriangle( nIdV) == SVT_NULL) - return false ; + return false ; } return true ; } //---------------------------------------------------------------------------- int -StmFromTriangleSoup::AddVertex( const Point3d& ptP) +StmFromTriangleSoup::AddVertex( const Point3d& ptP, const double dU, const double dV) { - // verifico se già presente + // verifico se gi� presente int nId ; if ( m_VertGrid.Find( ptP, 2 * EPS_SMALL, nId)) return nId ; // aggiungo il vertice - if ( ( nId = m_pSTM->AddVertex( ptP)) == SVT_NULL) + if ( ( nId = m_pSTM->AddVertex( ptP, dU, dV)) == SVT_NULL) return SVT_NULL ; m_VertGrid.InsertPoint( ptP, nId) ; return nId ; diff --git a/SurfAux.cpp b/SurfAux.cpp new file mode 100644 index 0000000..799a819 --- /dev/null +++ b/SurfAux.cpp @@ -0,0 +1,497 @@ +//---------------------------------------------------------------------------- +// EgalTech 2023-2023 +//---------------------------------------------------------------------------- +// File : SurfAux.cpp Data : 09.08.23 Versione : +// Contenuto : Implementazione di alcune funzioni di utilit? per le Superfici. +// +// +// +// Modifiche : 09.08.23 DB Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "CurveAux.h" +#include "GeoConst.h" +#include "CurveLine.h" +#include "CurveArc.h" +#include "CurveBezier.h" +#include "CurveComposite.h" +#include "/EgtDev/Include/EgtPointerOwner.h" +#include "/EgtDev/Include/EGkSurf.h" +#include "/EgtDev/Include/EGkSurfAux.h" +#include "/EgtDev/Include/EGkSurfBezier.h" + +using namespace std ; + +bool +NurbsSurfaceCanonicalize( SNurbsSurfData& snData) +{ + // per rendere una superficie non periodica devo recuperare i punti di controllo, suddivisi in isoparametriche lungo una direzione + // e applicare la trasformazione ad ogni isoparametrica, sostituendola poi a quella originale. ( si mantiene il numero di punti) + if ( snData.bPeriodicU || ! snData.bClampedU) { + bool bIsRational = snData.bRat ; + // vettore dei nodi + DBLVECTOR vU ; + int nKnot = (int) snData.vU.size() ; + for ( int k = 0 ; k < nKnot ; ++k ) { + double dKnot = snData.vU[k] ; + vU.push_back( dKnot) ; + } + for( int j = 0 ; j < snData.nCPV ; ++j) { + CNurbsData nuCurve ; + nuCurve.bPeriodic = true ; + nuCurve.nDeg = snData.nDegU ; + nuCurve.vU = vU ; + // vettore dei punti di controllo + PNTVECTOR vPtCtrl ; + // vettore dei pesi + DBLVECTOR vWeCtrl ; + for ( int i = 0 ; i < snData.nCPU ; ++i ) { + if ( bIsRational) { + vPtCtrl.push_back( snData.mCP[i][j] / snData.mW[i][j]) ; + vWeCtrl.push_back( snData.mW[i][j]) ; + } + else + vPtCtrl.push_back( snData.mCP[i][j]) ; + } + nuCurve.vCP = vPtCtrl ; + nuCurve.vW = vWeCtrl ; + // i punti dell' oggetto nuCurve devono essere in forma non omogenea + NurbsCurveCanonicalize( nuCurve) ; + for ( int i = 0 ; i < snData.nCPU ; ++i) { + snData.mCP[i][j] = nuCurve.vCP[i] ; + } + snData.vU = nuCurve.vU ; + } + snData.bPeriodicU = false ; + } + if ( snData.bPeriodicV || ! snData.bClampedV) { + bool bIsRational = snData.bRat ; + // vettore dei nodi + DBLVECTOR vV ; + int nKnot = (int) snData.vV.size() ; + for ( int k = 0 ; k < nKnot ; ++k ) { + double dKnot = snData.vV[k] ; + vV.push_back( dKnot) ; + } + for( int i = 0 ; i < snData.nCPU ; ++i) { + CNurbsData nuCurve ; + nuCurve.bPeriodic = true ; + nuCurve.nDeg = snData.nDegV ; + nuCurve.vU = vV ; + // vettore dei punti di controllo + PNTVECTOR vPtCtrl ; + // vettore dei pesi + DBLVECTOR vWeCtrl ; + for ( int j = 0 ; j < snData.nCPV ; ++j ) { + if ( bIsRational) { + vPtCtrl.push_back( snData.mCP[i][j] / snData.mW[i][j]) ; + vWeCtrl.push_back( snData.mW[i][j]) ; + } + else + vPtCtrl.push_back( snData.mCP[i][j]) ; + } + nuCurve.vCP = vPtCtrl ; + nuCurve.vW = vWeCtrl ; + // i punti dell' oggetto nuCurve devono essere in forma non omogenea + NurbsCurveCanonicalize( nuCurve) ; + for ( int j = 0 ; j < snData.nCPV ; ++j ) { + snData.mCP[i][j] = nuCurve.vCP[j] ; + } + snData.vV = nuCurve.vU ; + } + snData.bPeriodicV = false ; + } + return true; +} + + +//---------------------------------------------------------------------------- +ISurf* +NurbsToBezierSurface(const SNurbsSurfData& snData) +{ + // la superficie Nurbs deve essere in forma canonica + if ( snData.bPeriodicU || snData.bPeriodicV || snData.bExtraKnotes ) + return nullptr ; + // controllo sul numero dei nodi + int nU = snData.nCPU + snData.nDegU - 1 ; + int nV = snData.nCPV + snData.nDegV - 1 ; + // controllo nodi e punti di controllo + //if ( nU != int( snData.vU.size()) || nV != int( snData.vV.size()) || snData.nCPU * snData.nCPV != int( snData.vCP.size())) + if ( nU != int(snData.vU.size()) || nV != int(snData.vV.size())) { + return nullptr ; + } + + // verifico le condizioni agli estremi sui nodi (i primi nDeg nodi e gli ultimi nDeg nodi devono essere uguali tra loro) + bool bOk = true ; + // direzione U + for ( int i = 1 ; i < snData.nDegU ; ++ i) { + if ( abs( snData.vU[i] - snData.vU[0]) >= EPS_ZERO) + bOk = false ; + } + for ( int i = 1 ; i < snData.nDegU ; ++ i) { + if ( abs( snData.vU[nU - 1 - i] - snData.vU[nU - 1]) >= EPS_ZERO) + bOk = false ; + } + // direzione V + for ( int i = 1 ; i < snData.nDegV ; ++ i) { + if ( abs( snData.vV[i] - snData.vV[0]) >= EPS_ZERO) + bOk = false ; + } + for ( int i = 1 ; i < snData.nDegV ; ++ i) { + if ( abs( snData.vV[nV - 1 - i] - snData.vV[nV - 1]) >= EPS_ZERO) + bOk = false ; + } + if ( ! bOk) + return nullptr ; + + // algoritmo 5.7 del libro "The NURBS book"////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // creazione delle strips nella direzione U ( trasformo le curve iso con U costante in bezier) + int a = snData.nDegU - 1 ; + int b = snData.nDegU ; + int nb = 0 ; // numero di strisce in U ( lunghezza con U costante) + vector vCPV( snData.nCPV) ; + vector< vector> mBC (snData.nDegU + 1,vCPV ) ; + vector< vector> mBC_next (snData.nDegU - 1, vCPV) ; + vector< vector> mPC_strip(snData.nDegU + 1, vCPV) ; // matrice che verrà ingrandita e conterrà la superficie metà bezier e metà NURBS + DBLVECTOR vV_W( snData.nCPV) ; + vector mW( snData.nDegU + 1, vV_W) ; + vector mW_next( snData.nDegU - 1, vV_W) ; + vector mW_strip( snData.nDegU + 1, vV_W) ; + DBLVECTOR vAlpha ; + vAlpha.resize( snData.nDegU - 1) ; + if ( ! snData.bRat ) { + for ( int i = 0 ; i <= snData.nDegU ; ++i ) { + for ( int row = 0 ; row < snData.nCPV ; ++row ) { + mBC[i][row] = snData.mCP[i][row] ; + } + } + } + else { + for ( int i = 0 ; i <= snData.nDegU ; ++i ) { + for (int row = 0 ; row < snData.nCPV ; ++ row) { + mW[i][row] = snData.mW[i][row] ; + mBC[i][row] = snData.mCP[i][row] * snData.mW[i][row] ; + } + } + } + + bool bRef = false ; + while ( snData.nDegU != 1 ? b < nU - 1 : b < nU) { // qui correggo un probabile errore, mettendo nU anziché nCPV, come indicato nell'algoritmo + int i = b ; + while ( b < nU - 1 && abs( snData.vU[b+1] - snData.vU[b]) < EPS_ZERO) + ++ b ; + int mult = b - i + 1 ; + if ( mult < snData.nDegU ) { + bRef = true ; + // calcolo numeratore e alpha + double numer = snData.vU[b] - snData.vU[a] ; + for ( int j = snData.nDegU ; j > mult ; -- j) + vAlpha[j-mult-1] = numer / ( snData.vU[a+j] - snData.vU[a]) ; + int r = snData.nDegU - mult ; + for ( int j = 1 ; j <= snData.nDegU - mult ; ++j ) { + int save = r - j ; + int s = mult + j ; + if ( ! snData.bRat ) { + for ( int k = snData.nDegU ; k >= s ; --k ) { + for ( int row = 0 ; row < snData.nCPV ; ++row) { + mBC[k][row] = vAlpha[k-s] * mBC[k][row] + ( 1 - vAlpha[k-s]) * mBC[k-1][row] ; + } + } + } + else { + for ( int k = snData.nDegU ; k >= s ; --k ) { + for ( int row = 0 ; row < snData.nCPV ; ++row) { + mBC[k][row] = vAlpha[k-s] * mBC[k][row] + ( 1 - vAlpha[k-s]) * mBC[k-1][row] ; + mW[k][row] = vAlpha[k-s] * mW[k][row] + ( 1 - vAlpha[k-s]) * mW[k-1][row] ; + } + } + } + + if ( b < nU - 1 ) { + for ( int row = 0 ; row < snData.nCPV ; ++row) { + mBC_next[save][row] = mBC[snData.nDegU][row] ; + } + if ( snData.bRat ) + for ( int row = 0 ; row < snData.nCPV ; ++row) { + mW_next[save][row] = mW[snData.nDegU][row] ; + } + } + } + } + mPC_strip.resize( snData.nDegU * ( nb + 1) + 1 , vCPV) ; + mW_strip.resize( snData.nDegU * ( nb + 1) + 1, vV_W) ; + if ( ! snData.bRat) + for ( int i = 0 ; i <= snData.nDegU ; ++i) { + for ( int row = 0 ; row < snData.nCPV ; ++row ) { + mPC_strip[i+ nb * snData.nDegU][row] = mBC[i][row] ; + } + } + else { + for ( int i = 0 ; i <= snData.nDegU ; ++i) { + for ( int row = 0 ; row < snData.nCPV ; ++row ) { + mPC_strip[i+ nb * snData.nDegU][row] = mBC[i][row]/mW[i][row] ; + mW_strip[i+ nb * snData.nDegU][row] = mW[i][row] ; + } + } + } + ++ nb ; + // ho finito di definire la patch di Bezier attuale e passo alla successiva + + // aggiorno mBC con i valori della prossima pezza di Bezier // corrisponde a nb = nb + 1 + if ( ! snData.bRat){ + for (int i = 0 ; i < snData.nDegU - 1 ; ++ i) { + for ( int row = 0 ; row < snData.nCPV ; ++row) { + mBC[i][row] = mBC_next[i][row] ; + } + } + } + else { + for (int i = 0 ; i < snData.nDegU - 1 ; ++ i) { + for ( int row = 0 ; row < snData.nCPV ; ++row) { + mBC[i][row] = mBC_next[i][row] ; + mW[i][row] = mW_next[i][row] ; + } + } + } + + if ( b < nU - 1 ) { + for ( int i = snData.nDegU - mult ; i <= snData.nDegU ; ++ i) { + for (int row = 0 ; row < snData.nCPV ; ++ row ) { + mBC[i][row] = snData.mCP[b - snData.nDegU + i + 1][row] ; + } + } + if ( snData.bRat ) { + for ( int i = snData.nDegU - mult ; i <= snData.nDegU ; ++ i) { + for (int row = 0 ; row < snData.nCPV ; ++ row ) { + mW[i][row] = snData.mW[b - snData.nDegU + i + 1][row] ; + } + } + } + a = b ; + ++b ; + } + else if ( snData.nDegU == 1 && b < nU) { + a = b ; + ++b ; + } + } + + // se non ho raffinato allora tutti i nodi avevano gi? molteplicit? massima. Converto direttamente in Bezier la dir U + int nCPU_ref ; // numero dei punti di controllo in U dopo il raffinamento + if ( ! bRef ) { + nCPU_ref = snData.nCPU ; + mPC_strip.resize( snData.nCPU, vCPV) ; + mW_strip.resize( snData.nCPU, vV_W) ; + if ( ! snData.bRat) { + for ( int i = 0 ; i < nCPU_ref ; ++i) { + for ( int row = 0 ; row < snData.nCPV ; ++ row) { + mPC_strip[i][row] = snData.mCP[i][row] ; + } + } + } + else { + for ( int i = 0 ; i < nCPU_ref ; ++i) { + for ( int row = 0 ; row < snData.nCPV ; ++ row) { + mPC_strip[i][row] = snData.mCP[i][row] ; + mW_strip[i][row] = snData.mW[i][row] ; + } + } + } + } + else + nCPU_ref = snData.nDegU * nb + 1 ; // numero dei punti di controllo in U dopo il raffinamento + + // ora ho ottenuto le strisce nDegU x nCPV + // devo ripetere la procedura, sulla dir V, per ottenere le patch nDegU x nDegV + a = snData.nDegV - 1 ; + b = snData.nDegV ; + int nc = 0 ; // numero di strisce in V ( lunghezza con V costante) + vector vDegV(snData.nDegV + 1) ; + vector vDegV_1(snData.nDegV - 1) ; + vector< vector> m_BC1( nCPU_ref, vDegV) ; + vector< vector> m_BC1_next( nCPU_ref, vDegV_1) ; + DBLVECTOR vV1_W(snData.nDegV + 1) ; + DBLVECTOR vV2_W(snData.nDegV - 1) ; + vector mW1( nCPU_ref, vV1_W) ; + vector mW1_next( nCPU_ref, vV2_W) ; + DBLVECTOR vAlpha1( snData.nDegV - 1) ; + vector> mPC_tot( nCPU_ref, vDegV) ; + vector mW_tot( nCPU_ref, vV1_W) ; + if ( ! snData.bRat ) { + for ( int i = 0 ; i < nCPU_ref ; ++i ) { + for ( int row = 0 ; row <= snData.nDegV ; ++row ) { + m_BC1[i][row] = mPC_strip[i][row] ; + } + } + } + else { + for ( int i = 0 ; i < nCPU_ref ; ++i ) { + for (int row = 0 ; row <= snData.nDegV ; ++ row) { + mW1[i][row] = mW_strip[i][row] ; + m_BC1[i][row] = mPC_strip[i][row] * mW_strip[i][row] ; + } + } + } + + bRef = false ; + while ( snData.nDegV != 1 ? b < nV - 1 : b < nV) { // qui correggo un probabile errore, mettendo nU anziché nCPV, come indicato nell'algoritmo + int i = b ; + while ( b < nV - 1 && abs( snData.vV[b+1] - snData.vV[b]) < EPS_ZERO) + ++ b ; + int mult = b - i + 1 ; + if ( mult < snData.nDegV ) { + bRef = true ; + // calcolo numeratore e alpha + double numer = snData.vV[b] - snData.vV[a] ; + for ( int j = snData.nDegV ; j > mult ; -- j) + vAlpha1[j-mult-1] = numer / ( snData.vV[a+j] - snData.vV[a]) ; + int r = snData.nDegV - mult ; + for ( int j = 1 ; j <= snData.nDegV - mult ; ++j ) { + int save = r - j ; + int s = mult + j ; + if ( ! snData.bRat) { + for ( int k = 0 ; k < nCPU_ref ; ++k) { + for ( int row = snData.nDegV ; row >= s ; --row ) { + m_BC1[k][row] = vAlpha1[row-s] * m_BC1[k][row] + ( 1 - vAlpha1[row-s]) * m_BC1[k][row-1] ; + } + } + } + else { + for ( int k = 0 ; k < nCPU_ref ; ++k) { + for ( int row = snData.nDegV ; row >= s ; --row ) { + m_BC1[k][row] = vAlpha1[row-s] * m_BC1[k][row] + ( 1 - vAlpha1[row-s]) * m_BC1[k][row-1] ; + mW1[k][row] = vAlpha1[row-s] * mW1[k][row] + ( 1 - vAlpha1[row-s]) * mW1[k][row-1] ; + } + } + } + + if ( b < nV - 1 ) { + if ( !snData.bRat ){ + for ( int i = 0 ; i < nCPU_ref ; ++i) { + m_BC1_next[i][save] = m_BC1[i][snData.nDegV] ; + } + } + else { + for ( int i = 0 ; i < nCPU_ref ; ++i) { + m_BC1_next[i][save] = m_BC1[i][snData.nDegV] ; + mW1_next[i][save] = mW1[i][snData.nDegV] ; + } + } + } + } + } + int nRef = snData.nDegV * ( nc + 1) + 1 ; + for ( int k = 0 ; k < nCPU_ref; ++k){ + mPC_tot[k].resize( nRef) ; + mW_tot[k].resize( nRef) ; + } + if ( ! snData.bRat) + for ( int i = 0 ; i < nCPU_ref ; ++i) { + for ( int row = 0 ; row <= snData.nDegV ; ++row ) { + mPC_tot[i][row + nc * snData.nDegV] = m_BC1[i][row] ; + } + } + else { + for ( int i = 0 ; i < nCPU_ref ; ++i) { + for ( int row = 0 ; row <= snData.nDegV ; ++row ) { + mPC_tot[i][row + nc * snData.nDegV] = m_BC1[i][row]/mW1[i][row] ; + mW_tot[i][row + nc * snData.nDegV] = mW1[i][row] ; + } + } + } + ++ nc ; + // ho finito di definire la patch di Bezier attuale e passo alla successiva + + // aggiorno mBC con i valori della prossima pezza di Bezier // corrisponde a nc = nc + 1 + if ( ! snData.bRat){ + for (int i = 0 ; i < nCPU_ref ; ++ i) { + for ( int row = 0 ; row < snData.nDegV - 1 ; ++row) { + m_BC1[i][row] = m_BC1_next[i][row] ; + } + } + } + else { + for (int i = 0 ; i < nCPU_ref ; ++ i) { + for ( int row = 0 ; row < snData.nDegV - 1 ; ++row) { + m_BC1[i][row] = m_BC1_next[i][row] ; + mW1[i][row] = mW1_next[i][row] ; + } + } + } + + if ( b < nV - 1) { + if ( ! snData.bRat) { + for (int i = 0 ; i < nCPU_ref ; ++ i ) { + for ( int row = snData.nDegV - mult ; row <= snData.nDegV ; ++ row) { + m_BC1[i][row] = mPC_strip[i][b - snData.nDegV + row + 1] ; + } + } + } + else { + for (int i = 0 ; i < nCPU_ref ; ++ i ) { + for ( int row = snData.nDegV - mult ; row <= snData.nDegV ; ++ row) { + m_BC1[i][row] = mPC_strip[i][b - snData.nDegV + row + 1] * mW_strip[i][b - snData.nDegV + row + 1] ; + mW1[i][row] = mW_strip[i][b - snData.nDegV + row + 1] ; + } + } + } + a = b ; + ++b ; + } + else if ( snData.nDegV == 1 && b < nV) { + a = b ; + ++b ; + } + } + // se non ho raffinato allora aggiungo direttamente alle matrici della superficie totale + int nCPV_ref ; // numero dei punti di controllo in V dopo il raffinamento + if ( ! bRef) { + nCPV_ref = snData.nCPV ; + for ( int k = 0 ; k < nCPU_ref ; ++k){ + mPC_tot[k].resize( snData.nCPV) ; + mW_tot[k].resize( snData.nCPV) ; + } + if ( ! snData.bRat) { + for ( int i = 0 ; i < nCPU_ref ; ++i) { + for ( int row = 0 ; row < nCPV_ref ; ++ row) { + mPC_tot[i][row] = mPC_strip[i][row] ; + } + } + } + else { + for ( int i = 0 ; i < nCPU_ref ; ++i) { + for ( int row = 0 ; row < nCPV_ref ; ++ row) { + mPC_tot[i][row] = mPC_strip[i][row] ; + mW_tot[i][row] = mW_strip[i][row] ; + } + } + } + } + else + nCPV_ref = snData.nDegV * nc + 1 ; + + // finalmente setto la superficie di bezier totale divisa in nb patch in U e nc patch in V + PtrOwner pSrfBz( CreateSurfBezier()) ; + if ( IsNull( pSrfBz)) + return nullptr ; + pSrfBz->Init(snData.nDegU, snData.nDegV, nb, nc, snData.bRat) ; + if ( !snData.bRat ) { + for ( int i = 0 ; i < nCPU_ref; ++ i) { + for (int j = 0 ; j < nCPV_ref; ++j) { + pSrfBz->SetControlPoint( i + nCPU_ref * j, mPC_tot[i][j]) ; + } + } + } + else { + for ( int i = 0 ; i < nCPU_ref; ++ i) { + for (int j = 0 ; j < nCPV_ref; ++j) { + pSrfBz->SetControlPoint( i + nCPU_ref * j, mPC_tot[i][j] /*/ mW_tot[i][j]*/, mW_tot[i][j]) ; + } + } + } + return Release( pSrfBz) ; +} \ No newline at end of file diff --git a/SurfAux.h b/SurfAux.h new file mode 100644 index 0000000..09867fc --- /dev/null +++ b/SurfAux.h @@ -0,0 +1,33 @@ +//---------------------------------------------------------------------------- +// EgalTech 2023-2023 +//---------------------------------------------------------------------------- +// File : SurfAux.h Data : 09.08.23 Versione : +// Contenuto : Dichiarazione di alcune funzioni di utilità per le superfici. +// +// +// +// Modifiche : 09.08.23 DB Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#pragma once + +#include "/EgtDev/Include/EGkSurfAux.h" + +//---------------------------------------------------------------------------- +//bool IsClosed( const ICurve& crvC) ; +//bool IsValidParam( const ICurve& crvC, double dPar, ICurve::Side nSide) ; +//bool IsStartParam( const ICurve& crvC, double dPar) ; +//bool IsEndParam( const ICurve& crvC, double dPar) ; +//bool GetNearestExtremityToPoint( const Point3d& ptP, const ICurve& Curve, bool& bStart) ; +//bool MoveParamToAvoidTg( double& dU, ICurve::Side nSide, const ICurve& Curve) ; +//bool GetTang( const ICurve& crvC, double dU, ICurve::Side nS, Vector3d& vtTang) ; +//bool GetPointTang( const ICurve& crvC, double dU, ICurve::Side nS, Point3d& ptPos, Vector3d& vtTang) ; +//bool GetPointDiffGeom( const ICurve& crvC, double dU, ICurve::Side nS, CrvPointDiffGeom& oDiffG) ; +//bool ImproveCurveParamAtPoint( double& dU, const Point3d& ptP, const ICurve* pCrv) ; +//bool CurveGetAreaXY( const ICurve& crvC, double& dArea) ; +//bool CurveGetArea( const ICurve& crvC, Plane3d& plPlane, double& dArea) ; +//bool CurveDump( const ICurve& crvC, std::string& sOut, bool bMM, const char* szNewLine) ; +//bool CopyExtrusion( const ICurve* pSouCrv, ICurve* pDestCrv) ; +//bool CopyThickness( const ICurve* pSouCrv, ICurve* pDestCrv) ; diff --git a/SurfBezier.cpp b/SurfBezier.cpp index 228fad5..6ddb279 100644 --- a/SurfBezier.cpp +++ b/SurfBezier.cpp @@ -20,6 +20,9 @@ #include "Bernstein.h" #include "CurveBezier.h" #include "CurveComposite.h" +#include "Tree.h" +#include "Triangulate.h" +#include "SurfTriMesh.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkStmFromTriangleSoup.h" #include "/EgtDev/Include/EGkStringUtils3d.h" @@ -33,12 +36,13 @@ using namespace std ; GEOOBJ_REGISTER( SRF_BEZIER, NGE_S_BEZ, SurfBezier) ; //---------------------------------------------------------------------------- -SurfBezier::SurfBezier(void) +SurfBezier::SurfBezier( void) : m_pSTM( nullptr), m_nStatus( TO_VERIFY), m_nDegU(), m_nDegV(), m_nSpanU(), m_nSpanV(), m_bRat( false), m_bTrimmed( false), m_pTrimReg( nullptr) { m_nTempProp[0] = 0 ; m_nTempProp[1] = 0 ; + } //---------------------------------------------------------------------------- @@ -144,6 +148,15 @@ SurfBezier::SetTrimRegion( const ISurfFlatRegion& sfrTrimReg) return true ; } +//---------------------------------------------------------------------------- +SurfFlatRegion* +SurfBezier::GetTrimRegion( void) const +{ + if ( ! m_bTrimmed || m_pTrimReg == nullptr ) + return nullptr ; + return m_pTrimReg ; +} + //---------------------------------------------------------------------------- bool SurfBezier::GetInfo( int& nDegU, int& nDegV, int& nSpanU, int& nSpanV, bool& bIsRat, bool& bTrimmed) const @@ -1218,39 +1231,62 @@ SurfBezier::GetCurveOnV( double dU) const CurveComposite* SurfBezier::GetLoop( int nLoop) const { - // Se superficie completa, basta concatenare le 4 isoparametriche di bordo - if ( ! m_bTrimmed) { - // Esiste solo il loop esterno - if ( nLoop != 0) + // Il primo loop sono le 4 isoparametriche di bordo concatenate + if ( ! m_bTrimmed ) { + if ( nLoop != 0 ) return nullptr ; - // Loop + // Loop PtrOwner pLoop( CreateBasicCurveComposite()) ; - // prima curva isoparametrica in U con V=0 + // prima curva isoparametrica in U con V=0 PtrOwner pCrvCoU0( GetCurveOnU( 0)) ; if ( ! IsNull( pCrvCoU0) && ! pCrvCoU0->IsAPoint()) pLoop->AddCurve( Release( pCrvCoU0)) ; - // seconda curva isoparametrica in V con U=m_nSpanU + // seconda curva isoparametrica in V con U=m_nSpanU PtrOwner pCrvCoV1( GetCurveOnV( m_nSpanU)) ; if ( ! IsNull( pCrvCoV1) && ! pCrvCoV1->IsAPoint()) pLoop->AddCurve( Release( pCrvCoV1)) ; - // terza curva isoparametrica in U con V=m_nSpanV invertita + // terza curva isoparametrica in U con V=m_nSpanV invertita PtrOwner pCrvCoU1( GetCurveOnU( m_nSpanV)) ; if ( ! IsNull( pCrvCoU1) && ! pCrvCoU1->IsAPoint()) { pCrvCoU1->Invert() ; pLoop->AddCurve( Release( pCrvCoU1)) ; } - // quarta curva isoparametrica in V con U=0 invertita + // quarta curva isoparametrica in V con U=0 invertita PtrOwner pCrvCoV0( GetCurveOnV( 0)) ; if ( ! IsNull( pCrvCoV0) && ! pCrvCoV0->IsAPoint()) { pCrvCoV0->Invert() ; pLoop->AddCurve( Release( pCrvCoV0)) ; } - // se loop chiuso lo restituisco, altrimenti errore + // se loop chiuso lo restituisco, altrimenti errore return ( pLoop->IsClosed() ? Release( pLoop) : nullptr) ; } - // altrimenti trimmata, per ora non gestita - else - return nullptr ; + // la superficie è trimmata, quindi devo cercare nei vari chunck il loop corrispondente + else { + if ( nLoop > m_pTrimReg->GetChunkCount()) + return nullptr ; + else { + int nLoopCount = 0 ; + int nChunck = 0, nLoopLoc = 0; + INTVECTOR nLoopCountPerChunck ; + for ( int i = 0 ; i < m_pTrimReg->GetChunkCount() && nLoopCount != nLoop ; ++ i) { + int nLoopCountLoc = 0 ; + for ( int j = 0 ; j < m_pTrimReg->GetLoopCount( i) ; ++ j) { + ++ nLoopCountLoc ; + ++ nLoopCount ; + if ( nLoopCount != nLoop ) { + nChunck = i ; + nLoopLoc = j ; + break ; + } + } + nLoopCountPerChunck.push_back( nLoopCountLoc) ; + } + if ( nLoopCount < nLoop ) + return nullptr ; + PtrOwner pLoop( GetBasicCurveComposite( m_pTrimReg->GetLoop( nChunck, nLoopLoc))) ; + return Release( pLoop) ; + } + } } //---------------------------------------------------------------------------- @@ -1372,6 +1408,82 @@ SurfBezier::GetCurveOnVApproxLen( double dU) const return 0 ; return dLen ; } +// +////---------------------------------------------------------------------------- +//const SurfTriMesh* +//SurfBezier::GetAuxSurf( void) const +//{ +// // la superficie deve essere validata +// if ( m_nStatus != OK) { +// ResetAuxSurf() ; +// return nullptr ; +// } +// // se già calcolata, la restituisco +// if ( m_pSTM != nullptr) +// return m_pSTM ; +// // costruttore della superficie +// StmFromTriangleSoup stmSoup ; +// if ( ! stmSoup.Start()) +// return nullptr ; +// // definisco il numero degli step in U e in V +// double dMaxLenU = 0 ; +// for ( int j = 0 ; j <= m_nDegV * m_nSpanV ; ++ j) +// dMaxLenU = max( dMaxLenU, GetCurveOnUApproxLen( double( j) / m_nDegV)) ; +// int nStepU = GetSteps( m_nDegU, m_nSpanU, dMaxLenU, 2) ; +// double dMaxLenV = 0 ; +// for ( int i = 0 ; i <= m_nDegU * m_nSpanU ; ++ i) +// dMaxLenV = max( dMaxLenV, GetCurveOnVApproxLen( double( i) / m_nDegU)) ; +// int nStepV = GetSteps( m_nDegV, m_nSpanV, dMaxLenV, 2) ; +// // prima curva isoparametrica (potrebbe essere un solo punto) +// PolyLine PL1 ; +// GetCurveOnU( 0, nStepU, PL1) ; +// bool bSingle1 = ( PL1.GetPointNbr() == 1) ; +// // ciclo sulle isoparametriche +// for ( int i = 1 ; i <= nStepV ; ++ i) { +// // seconda curva isoparametrica (con tanti punti quanti la prima, oppure uno solo) +// double dV = double( i) * m_nSpanV / nStepV ; +// PolyLine PL2 ; +// GetCurveOnU( dV, nStepU, PL2) ; +// bool bSingle2 = ( PL2.GetPointNbr() == 1) ; +// // inserisco i triangoli della striscia nel costruttore della TriMesh +// Point3d ptP1c, ptP2c ; +// Point3d ptP1n, ptP2n ; +// bool bNext = PL1.GetFirstPoint( ptP1c) && PL2.GetFirstPoint( ptP2c) ; +// if ( bNext) { +// if ( bSingle1 && bSingle2) +// bNext = false ; +// if ( bSingle1) +// ptP1n = ptP1c ; +// else +// bNext = bNext && PL1.GetNextPoint( ptP1n) ; +// if ( bSingle2) +// ptP2n = ptP2c ; +// else +// bNext = bNext && PL2.GetNextPoint( ptP2n) ; +// } +// while ( bNext) { +// // eventuale primo triangolo (con base sui correnti e vertice su P2 successivo) +// if ( ! AreSamePointApprox( ptP1c, ptP2c)) +// stmSoup.AddTriangle( ptP2c, ptP1c, ptP2n) ; +// // eventuale secondo triangolo (con vertice su P1 corrente e base sui successivi) +// if ( ! AreSamePointApprox( ptP1n, ptP2n)) +// stmSoup.AddTriangle( ptP1c, ptP1n, ptP2n) ; +// // passo alla successiva coppia +// ptP1c = ptP1n ; +// ptP2c = ptP2n ; +// bNext = ( bSingle1 || PL1.GetNextPoint( ptP1n)) && ( bSingle2 || PL2.GetNextPoint( ptP2n)) ; +// } +// // salvo isoparametrica PL2 in PL1 +// PL1.GetUPointList().swap( PL2.GetUPointList()) ; +// bSingle1 = bSingle2 ; +// } +// // la completo +// if ( ! stmSoup.End()) +// return nullptr ; +// // la salvo +// m_pSTM = GetBasicSurfTriMesh( stmSoup.GetSurf()) ; +// return m_pSTM ; +//} //---------------------------------------------------------------------------- const SurfTriMesh* @@ -1385,70 +1497,94 @@ SurfBezier::GetAuxSurf( void) const // se già calcolata, la restituisco if ( m_pSTM != nullptr) return m_pSTM ; - // costruttore della superficie + + // costruttore della superficie + vector vvPL ; + POLYLINEVECTOR vPL ; // per usare i polygon basic + Tree Tree( this, true) ; + std::vector> vTrees ; + Tree.GetIndependentTrees( vTrees) ; + for ( int i = 0 ; i < (int) vTrees.size() ; ++ i) { + Point3d ptMin = std::get<0>( vTrees[i]) ; + Point3d ptMax = std::get<1>( vTrees[i]) ; + Tree.SetSurf( this, true, ptMin, ptMax) ; + //Tree.BuildTree_test() ; // per debug + //Tree.BuildTree( 5 * LIN_TOL_FINE, 1) ; + Tree.BuildTree( LIN_TOL_FINE, 0.1) ; + Tree.GetPolygons( vvPL) ; + //Tree.GetPolygonsBasic( vPL) ; // per usare i polygon basic + } + //// per usare i polygon basic////////////////////// + //for (int k = 0 ; k < (int)vPL.size(); ++k) { + // vvPL.emplace_back() ; + // vvPL.back().push_back(vPL[k]) ; + //} + //// per usare i polygon basic/////////////////// + + PtrOwner pSrfTm( CreateBasicSurfTriMesh()) ; StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start()) return nullptr ; - // definisco il numero degli step in U e in V - double dMaxLenU = 0 ; - for ( int j = 0 ; j <= m_nDegV * m_nSpanV ; ++ j) - dMaxLenU = max( dMaxLenU, GetCurveOnUApproxLen( double( j) / m_nDegV)) ; - int nStepU = GetSteps( m_nDegU, m_nSpanU, dMaxLenU, 2) ; - double dMaxLenV = 0 ; - for ( int i = 0 ; i <= m_nDegU * m_nSpanU ; ++ i) - dMaxLenV = max( dMaxLenV, GetCurveOnVApproxLen( double( i) / m_nDegU)) ; - int nStepV = GetSteps( m_nDegV, m_nSpanV, dMaxLenV, 2) ; - // prima curva isoparametrica (potrebbe essere un solo punto) - PolyLine PL1 ; - GetCurveOnU( 0, nStepU, PL1) ; - bool bSingle1 = ( PL1.GetPointNbr() == 1) ; - // ciclo sulle isoparametriche - for ( int i = 1 ; i <= nStepV ; ++ i) { - // seconda curva isoparametrica (con tanti punti quanti la prima, oppure uno solo) - double dV = double( i) * m_nSpanV / nStepV ; - PolyLine PL2 ; - GetCurveOnU( dV, nStepU, PL2) ; - bool bSingle2 = ( PL2.GetPointNbr() == 1) ; - // inserisco i triangoli della striscia nel costruttore della TriMesh - Point3d ptP1c, ptP2c ; - Point3d ptP1n, ptP2n ; - bool bNext = PL1.GetFirstPoint( ptP1c) && PL2.GetFirstPoint( ptP2c) ; - if ( bNext) { - if ( bSingle1 && bSingle2) - bNext = false ; - if ( bSingle1) - ptP1n = ptP1c ; - else - bNext = bNext && PL1.GetNextPoint( ptP1n) ; - if ( bSingle2) - ptP2n = ptP2c ; - else - bNext = bNext && PL2.GetNextPoint( ptP2n) ; + + // prendo i punti di ogni polyline dell'albero, li triangolo e li porto in 3d + for ( POLYLINEVECTOR vPL : vvPL) { + PNTVECTOR vPnt ; + INTVECTOR vTria ; + Triangulate Tri ; + if ( ! Tri.Make( vPL, vPnt, vTria)) + return nullptr ; + + // porto i punti in 3d + PNTVECTOR vPnt3d ; + for ( int i = 0 ; i < int( vPnt.size()) ; ++ i) { + Point3d pt3d ; + if ( ! GetPointD1D2( vPnt[i].x / SBZ_TREG_COEFF, vPnt[i].y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3d)) + return nullptr ; + vPnt3d.push_back( pt3d) ; } - while ( bNext) { - // eventuale primo triangolo (con base sui correnti e vertice su P2 successivo) - if ( ! AreSamePointApprox( ptP1c, ptP2c)) - stmSoup.AddTriangle( ptP2c, ptP1c, ptP2n) ; - // eventuale secondo triangolo (con vertice su P1 corrente e base sui successivi) - if ( ! AreSamePointApprox( ptP1n, ptP2n)) - stmSoup.AddTriangle( ptP1c, ptP1n, ptP2n) ; - // passo alla successiva coppia - ptP1c = ptP1n ; - ptP2c = ptP2n ; - bNext = ( bSingle1 || PL1.GetNextPoint( ptP1n)) && ( bSingle2 || PL2.GetNextPoint( ptP2n)) ; + int nTria = int( vTria.size()) / 3 ; + for ( int i = 0 ; i < nTria ; ++i) { + if ( ! stmSoup.AddTriangle( vPnt3d[vTria[3*i]], vPnt3d[vTria[3*i+1]], vPnt3d[vTria[3*i+2]], + vPnt[vTria[3*i]].x, vPnt[vTria[3*i]].y, + vPnt[vTria[3*i+1]].x, vPnt[vTria[3*i+1]].y, + vPnt[vTria[3*i+2]].x, vPnt[vTria[3*i+2]].y)) + return nullptr ; } - // salvo isoparametrica PL2 in PL1 - PL1.GetUPointList().swap( PL2.GetUPointList()) ; - bSingle1 = bSingle2 ; - } - // la completo + } + + // la salvo if ( ! stmSoup.End()) return nullptr ; - // la salvo m_pSTM = GetBasicSurfTriMesh( stmSoup.GetSurf()) ; + return m_pSTM ; } +//---------------------------------------------------------------------------- +bool +SurfBezier::GetLeaves( std::vector>& vLeaves) const +{ + std::vector vCells ; + Tree Tree( this, true) ; + std::vector> vTrees ; + Tree.GetIndependentTrees( vTrees) ; + for ( int i = 0 ; i < (int) vTrees.size() ; ++ i) { + Point3d ptMin = std::get<0>( vTrees[i]) ; + Point3d ptMax = std::get<1>( vTrees[i]) ; + Tree.SetSurf( this, true, ptMin, ptMax) ; + //Tree.BuildTree_test() ; + //Tree.BuildTree( 5 * LIN_TOL_FINE, 1) ; + Tree.BuildTree( LIN_TOL_FINE, 0.1) ; + Tree.GetLeaves( vCells) ; + for (int k = 0 ; k < (int)vCells.size(); ++ k ) { + std::tuple tCell ; + tCell = make_tuple( vCells[k].m_nId, vCells[k].GetBottomLeft(), vCells[k].GetTopRight()) ; + vLeaves.push_back( tCell) ; + } + } + return true ; +} + //---------------------------------------------------------------------------- void SurfBezier::ResetAuxSurf( void) const diff --git a/SurfBezier.h b/SurfBezier.h index d4d929d..92e8dea 100644 --- a/SurfBezier.h +++ b/SurfBezier.h @@ -83,6 +83,7 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW { return SetControlPoint( GetInd( nIndU, nIndV), ptCtrl, dW) ; } bool SetControlPoint( int nInd, const Point3d& ptCtrl, double dW) override ; bool SetTrimRegion( const ISurfFlatRegion& sfrTrimReg) override ; + SurfFlatRegion* GetTrimRegion( void) const override ; bool GetInfo( int& nDegU, int& nDegV, int& nSpanU, int& nSpanV, bool& bIsRat, bool& bTrimmed) const override ; const Point3d& GetControlPoint( int nIndU, int nIndV, bool* pbOk) const override { return GetControlPoint( GetInd( nIndU, nIndV), pbOk) ; } @@ -105,6 +106,7 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW bool GetControlCurveOnU( int nIndV, PolyLine& plCtrlU) const override ; bool GetControlCurveOnV( int nIndU, PolyLine& plCtrlV) const override ; const SurfTriMesh* GetAuxSurf( void) const override ; + bool GetLeaves( std::vector>& vLeaves) const override ; public : // IGeoObjRW int GetNgeId( void) const override ; @@ -166,7 +168,7 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW PNTVECTOR m_vPtCtrl ; // vettore dei punti di controllo DBLVECTOR m_vWeCtrl ; // vettore dei pesi di controllo SurfFlatRegion* m_pTrimReg ; // eventuale regione di trim - int m_nTempProp[2] ; // vettore proprietà temporanee + int m_nTempProp[2] ; // vettore proprietà temporanee } ; //----------------------------------------------------------------------------- diff --git a/SurfTriMesh.cpp b/SurfTriMesh.cpp index 1e320fe..71aeb41 100644 --- a/SurfTriMesh.cpp +++ b/SurfTriMesh.cpp @@ -66,7 +66,7 @@ SurfTriMesh::Init( int nNumVert, int nNumTria, int nNumFacet) // se superficie vuota if ( nNumVert == 0 && nNumTria == 0 && nNumFacet == 0) return true ; - // verifico validità parametri + // verifico validit� parametri if ( nNumVert < 3 || nNumTria < 1) return false ; // prealloco la memoria @@ -107,28 +107,32 @@ SurfTriMesh::Clear( void) //---------------------------------------------------------------------------- int -SurfTriMesh::AddVertex( const Point3d& ptVert) +SurfTriMesh::AddVertex( const Point3d& ptVert, const double dU, const double dV) { - // imposto ricalcolo + // imposto ricalcolo m_nStatus = TO_VERIFY ; m_nParts = - 1 ; m_OGrMgr.Reset() ; ResetHashGrids3d() ; - // inserisco il vertice + // inserisco il vertice try { m_vVert.emplace_back( ptVert) ;} catch(...) { return SVT_NULL ;} // ne determino l'indice - return int( m_vVert.size() - 1) ; + int nId = int( m_vVert.size() - 1) ; + // aggiugo le coordinate corrispondenti allo spazio parametrico + m_vVert[nId].dU = dU ; + m_vVert[nId].dV = dV ; + return nId ; } //---------------------------------------------------------------------------- bool SurfTriMesh::MoveVertex( int nInd, const Point3d& ptNewVert) { - // verifico validità indice + // verifico validit� indice if ( nInd < 0 || nInd >= int( m_vVert.size())) return false ; - // verifico non sia già cancellato + // verifico non sia gi� cancellato if ( m_vVert[nInd].nIdTria == SVT_DEL) return false ; // sposto il vertice @@ -253,14 +257,14 @@ SurfTriMesh::RemoveTriangle( int nId) // verifico esistenza del triangolo if ( nId < 0 || nId >= GetTriangleSize()) return false ; - // verifico se già cancellato + // verifico se gi� cancellato if ( m_vTria[nId].nIdVert[0] == SVT_DEL) return true ; // aggiorno eventuali riferimenti dei vertici for ( int i = 0 ; i < 3 ; ++ i) { // indice vertice int nV = m_vTria[nId].nIdVert[i] ; - // se vertice non c'è passo al prossimo + // se vertice non c'� passo al prossimo if ( nV < 0 || nV >= int( m_vVert.size())) continue ; if ( m_vVert[nV].nIdTria == nId) { @@ -278,7 +282,7 @@ SurfTriMesh::RemoveTriangle( int nId) for ( int i = 0 ; i < 3 ; ++ i) { // indice triangolo adiacente int nAdjT = m_vTria[nId].nIdAdjac[i] ; - // se triangolo adiacente non c'è passo al prossimo + // se triangolo adiacente non c'� passo al prossimo if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdVert[0] == SVT_DEL) continue ; // ne sistemo la contro-adiacenza @@ -291,7 +295,7 @@ SurfTriMesh::RemoveTriangle( int nId) m_vTria[nId].nIdVert[0] = SVT_DEL ; // invalido calcolo facce m_bFaceted = false ; - // invalido calcolo connettività + // invalido calcolo connettivit� m_nParts = - 1 ; return true ; } @@ -357,7 +361,7 @@ SurfTriMesh::GetCentroid( Point3d& ptCen) const // la superficie deve essere validata if ( m_nStatus != OK) return false ; - // se la superficie è chiusa, calcolo il centroide del solido + // se la superficie � chiusa, calcolo il centroide del solido if ( IsClosed()) { // applico le formule di R. Nurnberg Imperial College London ad ogni faccia Triangle3d Tria ; @@ -475,6 +479,20 @@ SurfTriMesh::GetVertex( int nId, Point3d& ptP) const return true ; } +//---------------------------------------------------------------------------- +bool +SurfTriMesh::GetVertexParam( int nId, double& dU, double& dV) const +{ + // verifico esistenza del vertice + if ( nId < 0 || nId >= GetVertexSize() || m_vVert[nId].nIdTria == SVT_DEL) + return false ; + // recupero i dati + dU = m_vVert[nId].dU ; + dV = m_vVert[nId].dV ; + return true ; +} + + //---------------------------------------------------------------------------- int SurfTriMesh::GetFirstVertex( Point3d& ptP) const @@ -482,6 +500,13 @@ SurfTriMesh::GetFirstVertex( Point3d& ptP) const return GetNextVertex( SVT_NULL, ptP) ; } +//---------------------------------------------------------------------------- +int +SurfTriMesh::GetFirstVertexParam( int nId, double& dU, double& dV) const +{ + return GetNextVertexParam( SVT_NULL, dU, dV) ; +} + //---------------------------------------------------------------------------- int SurfTriMesh::GetNextVertex( int nId, Point3d& ptP) const @@ -499,6 +524,24 @@ SurfTriMesh::GetNextVertex( int nId, Point3d& ptP) const return nId ; } +//---------------------------------------------------------------------------- +int +SurfTriMesh::GetNextVertexParam( int nId, double& dU, double& dV) const +{ + // cerco il primo successivo valido + do { + nId ++ ; + } while ( nId < GetVertexSize() && m_vVert[nId].nIdTria == SVT_DEL) ; + // se oltrepassata fine + if ( nId >= GetVertexSize()) + return SVT_NULL ; + // recupero i dati + dU = m_vVert[nId].dU ; + dV = m_vVert[nId].dV ; + // ritorno indice triangolo corrente + return nId ; +} + //---------------------------------------------------------------------------- bool SurfTriMesh::GetTriangle( int nId, int nIdVert[3]) const @@ -731,13 +774,13 @@ SurfTriMesh::GetTriangleBoundaryEdges( int nId, TriFlags3d& TFlags) const for ( int i = 0 ; i < 3 ; ++ i) { // indice triangolo adiacente al lato int nT = m_vTria[nId].nIdAdjac[i] ; - // se già definite le facce, verifico indice faccia + // se gi� definite le facce, verifico indice faccia if ( m_bFaceted) { TFlags.bFlag[i] = ( nT == SVT_NULL || m_vTria[nId].nIdFacet != m_vTria[nT].nIdFacet) ; } // altrimenti verifico con le normali else - // se non c'è triangolo adiacente o se forma un angolo oltre il limite, il lato è un contorno + // se non c'� triangolo adiacente o se forma un angolo oltre il limite, il lato � un contorno TFlags.bFlag[i] = ( nT == SVT_NULL || m_vTria[nId].vtN * m_vTria[nT].vtN < m_dCosBndAng) ; } @@ -821,7 +864,7 @@ SurfTriMesh::GetTriangleSmoothNormal( int nT, int nV, Vector3d& vtN) const if ( nPos == -1) return false ; - // medio le normali, finché non incontro degli spigoli + // medio le normali, finch� non incontro degli spigoli vtN = m_vTria[nT].vtN ; // parto dal triangolo e vado in direzione positiva int nLim = nPos ; @@ -852,7 +895,7 @@ SurfTriMesh::GetTriangleSmoothNormal( int nT, int nV, Vector3d& vtN) const SurfTriMesh* SurfTriMesh::CloneTriangle( int nT) const { - // verifico validità superficie ed esistenza del triangolo + // verifico validit� superficie ed esistenza del triangolo if ( ! IsValid() || nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL) return nullptr ; @@ -959,7 +1002,7 @@ SurfTriMesh::GetLoops( POLYLINEVECTOR& vPL) const if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back())) return false ; } - // se il lato 0 è di contorno + // se il lato 0 � di contorno else if ( nAdjT[0] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; @@ -971,7 +1014,7 @@ SurfTriMesh::GetLoops( POLYLINEVECTOR& vPL) const if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back())) return false ; } - // se il lato 1 è di contorno + // se il lato 1 � di contorno else if ( nAdjT[1] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; @@ -983,7 +1026,7 @@ SurfTriMesh::GetLoops( POLYLINEVECTOR& vPL) const if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back())) return false ; } - // se il lato 2 è di contorno + // se il lato 2 � di contorno else if ( nAdjT[2] == SVT_NULL) { // ho trovato l'inizio di un loop vPL.emplace_back() ; @@ -995,7 +1038,7 @@ SurfTriMesh::GetLoops( POLYLINEVECTOR& vPL) const if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back())) return false ; } - // altrimenti non c'è contorno + // altrimenti non c'� contorno else { // marco il triangolo come verificato m_vTria[nT].nTemp = m_nTimeStamp ; @@ -1040,10 +1083,10 @@ SurfTriMesh::MarchOneTria( int& nT, int& nV, int nTimeStamp, return false ; // vertice di fine adiacenza e indice del successivo lato int nAdjV = Next( nAdjS) ; - // verifico se il lato successivo è un bordo + // verifico se il lato successivo � un bordo int nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ; if ( nNextT == SVT_NULL) { - // se già recuperato + // se gi� recuperato if ( m_vTria[nAdjT].nTemp == nTimeStamp) { bEnd = true ; return true ; @@ -1091,8 +1134,8 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& frOCS.Translate( b3Box.GetMin().z * frOCS.VersZ()) ; #if 0 - // *** 1° Sfruttando le adiacenze *** - // Non funziona perchè genera loop con anelli + // *** 1� Sfruttando le adiacenze *** + // Non funziona perch� genera loop con anelli // Copio la superficie PtrOwner pStm( Clone()) ; if ( IsNull( pStm)) @@ -1100,7 +1143,7 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& // Elimino i triangoli con normale non equiversa alla direzione scelta for ( int i = 0 ; i < int( pStm->m_vTria.size()) ; ++ i) { - // se già cancellato, passo oltre + // se gi� cancellato, passo oltre if ( pStm->m_vTria[i].nIdVert[0] == SVT_DEL) continue ; // verifico la normale @@ -1159,7 +1202,7 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL.clear() ; #endif - // *** 2° Mediante unione delle regioni dei triangoli proiettati *** + // *** 2� Mediante unione delle regioni dei triangoli proiettati *** // calcolo la regione dei triangoli proiettati PtrOwner pSfr ; Triangle3d Tria ; @@ -1289,7 +1332,7 @@ SurfTriMesh::Dump( string& sOut, bool bMM, const char* szNewLine) const // segnalo eventuale incongruenza di orientamento if ( ! m_bOriented) sOut += string( "Inconsistent Orientation") + szNewLine ; - // segnalo numero di parti se più di una + // segnalo numero di parti se pi� di una int nParts = GetPartCount() ; if ( nParts > 1) sOut += string( "Parts =") + ToString( nParts) + szNewLine ; @@ -1519,7 +1562,7 @@ SurfTriMesh::Validate( bool bCorrect) // Verifico che i triangoli riferiti dalle facce esistano for ( int i = 0 ; i < GetFacetSize() && m_nStatus == OK && m_bFaceted ; ++ i) { - // verifico validità triangolo riferito + // verifico validit� triangolo riferito if ( m_vFacet[i] <= SVT_NULL || m_vFacet[i] >= GetTriangleSize() || m_vTria[ m_vFacet[i]].nIdVert[0] == SVT_DEL) @@ -1674,7 +1717,7 @@ SurfTriMesh::AdjustAdjacencies( void) bool SurfTriMesh::AdjustOrientations( void) { - // se non ci sono almeno 2 triangoli è inutile fare test + // se non ci sono almeno 2 triangoli � inutile fare test if ( m_vTria.size() < 2) { m_bOriented = true ; return true ; @@ -1718,7 +1761,7 @@ SurfTriMesh::AdjustTriaOrientation( TRINTDEQUE& S3iQ) S3iQ.pop_front() ; // assegno time stamp al triangolo m_vTria[nT].nTemp = m_nTimeStamp ; - // se c'è triangolo di riferimento, devo verificare se da invertire + // se c'� triangolo di riferimento, devo verificare se da invertire if ( nRefT != SVT_NULL) { // cerco indice half-edge in comune int nE = 0 ; @@ -1736,7 +1779,7 @@ SurfTriMesh::AdjustTriaOrientation( TRINTDEQUE& S3iQ) bool bOk = true ; for ( int j = 0 ; j < 3 ; ++ j) { int nAdjT = m_vTria[nT].nIdAdjac[j] ; - // se non c'è adiacenza o va sul triangolo di provenienza + // se non c'� adiacenza o va sul triangolo di provenienza if ( nAdjT == SVT_NULL || nAdjT == nRefT) ; // la verifico @@ -1791,7 +1834,7 @@ SurfTriMesh::TestSealing( void) bool SurfTriMesh::AdjustTopology( void) { - // se non è rimasto alcunché di valido, pulisco tutto ed esco + // se non � rimasto alcunch� di valido, pulisco tutto ed esco if ( GetVertexCount() < 3 || GetTriangleCount() < 1) { Clear() ; m_bOriented = true ; @@ -1808,7 +1851,7 @@ SurfTriMesh::AdjustTopology( void) // verifica adiacenze if ( ! AdjustAdjacencies()) return false ; - // verifica continuità orientazione + // verifica continuit� orientazione if ( ! AdjustOrientations()) return false ; // verifica chiusura @@ -1846,7 +1889,7 @@ SurfTriMesh::PackVertices( void) vVId.push_back( SVT_DEL) ; } } - // se non c'è stata compattazione, esco + // se non c'� stata compattazione, esco if ( nFirstFree == SVT_NULL) return true ; // lunghezza vettore indici vertici @@ -1861,7 +1904,7 @@ SurfTriMesh::PackVertices( void) // salto i triangoli cancellati if ( vOId[0] == SVT_DEL) continue ; - // verifico la validità degli indici + // verifico la validit� degli indici if ( vOId[0] < 0 || vOId[0] >= nVIdSize || vOId[1] < 0 || vOId[1] >= nVIdSize || vOId[2] < 0 || vOId[2] >= nVIdSize) @@ -1901,7 +1944,7 @@ SurfTriMesh::PackTriangles( void) vTId.push_back( SVT_DEL) ; } } - // se non c'è stata compattazione, esco + // se non c'� stata compattazione, esco if ( nFirstFree == SVT_NULL) return true ; // Invalido HashGrid @@ -1915,7 +1958,7 @@ SurfTriMesh::PackTriangles( void) // salto vertice cancellato if ( nOId == SVT_DEL) continue ; - // verifico la validità dell'indice + // verifico la validit� dell'indice if ( nOId < 0 || nOId >= nTIdSize) return false ; // aggiorno @@ -1945,7 +1988,7 @@ SurfTriMesh::PackTriangles( void) // salto le facets non valide if ( m_vFacet[nId] == SVT_DEL) continue ; - // verifico validità indice a triangolo + // verifico validit� indice a triangolo if ( m_vFacet[nId] < 0 || m_vFacet[nId] >= nTIdSize) return false ; // aggiorno @@ -2047,7 +2090,7 @@ SurfTriMesh::CreateByExtrusion( const PolyLine& PL, const Vector3d& vtExtr) m_OGrMgr.Reset() ; ResetHashGrids3d() ; - // verifico se la polilinea è chiusa + // verifico se la polilinea � chiusa bool bClosed = PL.IsClosed() ; // costruisco la mesh @@ -2080,7 +2123,7 @@ SurfTriMesh::CreateByExtrusion( const PolyLine& PL, const Vector3d& vtExtr) } // se curva chiusa, aggiungo gli ultimi due triangoli if ( bClosed) { - // non devo aggiungere i vertici, perchè coincidono con quelli iniziali + // non devo aggiungere i vertici, perch� coincidono con quelli iniziali // aggiungo i due triangoli relativi nIdV[0] = nV ; nIdV[1] = nV - 1 ; @@ -2099,7 +2142,7 @@ SurfTriMesh::CreateByExtrusion( const PolyLine& PL, const Vector3d& vtExtr) bool SurfTriMesh::CreateByPointCurve( const Point3d& ptP, const PolyLine& PL) { - // verifico validità punto/polilinea + // verifico validit� punto/polilinea bool bClosed = PL.IsClosed() ; // se chiusa, la polilinea deve avere almeno 3 punti if ( bClosed) { @@ -2143,7 +2186,7 @@ SurfTriMesh::CreateByPointCurve( const Point3d& ptP, const PolyLine& PL) // aggiorno indice punto precedente su curva nIdV[1] = nIdV[2] ; } - // se chiusa aggiungo l'ultimo triangolo (non il vertice perchè è il primo della curva) + // se chiusa aggiungo l'ultimo triangolo (non il vertice perch� � il primo della curva) if ( bClosed) { nIdV[2] = 1 ; // inserisco il triangolo A2p -> A1p -> A1s @@ -2235,7 +2278,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR } // ciclo sui punti while ( bNext1 || bNext2) { - // se non c'è V2s oppure c'è nuovo V1s e la diagonale più corta è V2p -> V1s + // se non c'� V2s oppure c'� nuovo V1s e la diagonale pi� corta � V2p -> V1s if ( ! bNext2 || ( bNext1 && ( nP1s == vPnt2[nP2p].second || vPnt1[nP1s].second == nP2p))) { // inserisco il vertice V1s (se ultimo e curve chiuse, prendo il primo) if ( nP1s == nTotP1 - 1 && bClosed) @@ -2257,7 +2300,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ; bNext1 = ( nP1s < nTotP1) ; } - // altrimenti è V1p -> V2s + // altrimenti � V1p -> V2s else { // inserisco il vertice V2s (se ultimo e curve chiuse, prendo il primo) if ( nP2s == nTotP2 - 1 && bClosed) @@ -2284,7 +2327,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR // altrimenti rigata con parametrizzazione sincrona sulle due curve else { - // verifico validità polilinee (devono avere almeno 2 punti e non coincidere se non agli estremi aperti) + // verifico validit� polilinee (devono avere almeno 2 punti e non coincidere se non agli estremi aperti) if ( ! VerifyPolylinesForTwoCurves( PL1, PL2)) return false ; @@ -2409,7 +2452,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR if ( bNext2) dA2s = ( dU2s - dU2F) / dDeltaU2 ; } - // se non c'è dA2s oppure c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s + // se non c'� dA2s oppure c'� nuovo dA1s e la diagonale pi� corta � dA2p -> dA1s else if ( ! bNext2 || ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM)) { // inserisco il vertice A1s if ( ( nV1s = AddVertex( ptP1s)) == SVT_NULL) @@ -2433,7 +2476,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR if ( bNext1) dA1s = ( dU1s - dU1F) / dDeltaU1 ; } - // altrimenti è dA1p -> dA2s + // altrimenti � dA1p -> dA2s else { // inserisco il vertice A2s if ( ( nV2s = AddVertex( ptP2s)) == SVT_NULL) @@ -2462,7 +2505,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR if ( bClosed) { dA1s = 1 ; dA2s = 1 ; - // se la diagonale più corta è dA2p -> dA1s = 0 + // se la diagonale pi� corta � dA2p -> dA1s = 0 if ( ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) { // inserisco il triangolo A2p -> A1p -> A1s = 0 nIdV[0] = nV2p ; @@ -2477,7 +2520,7 @@ SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nR if ( AddTriangle( nIdV) == SVT_NULL) return false ; } - // altrimenti è dA1p -> dA2s = 1 + // altrimenti � dA1p -> dA2s = 1 else { // inserisco il triangolo A2p -> A1p -> A2s = 1 nIdV[0] = nV2p ; @@ -2552,7 +2595,7 @@ SurfTriMesh::VerifyPolylinesForTwoCurves( const PolyLine& PL1, const PolyLine& P return false ; // verifiche sui punti successivi (non sugli ultimi) while ( bNext1 || bNext2) { - // se c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s oppure non c'è dA2s + // se c'� nuovo dA1s e la diagonale pi� corta � dA2p -> dA1s oppure non c'� dA2s if ( ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) || ! bNext2) { // verifico se coincidono if ( AreSamePointApprox( ptP2p, ptP1s)) @@ -2563,7 +2606,7 @@ SurfTriMesh::VerifyPolylinesForTwoCurves( const PolyLine& PL1, const PolyLine& P if ( bNext1) dA1s = ( dU1s - dU1F) / dDeltaU1 ; } - // altrimenti è dA1p -> dA2s = 1 + // altrimenti � dA1p -> dA2s = 1 else { // verifico se coincidono if ( AreSamePointApprox( ptP1p, ptP2s)) @@ -2667,7 +2710,7 @@ AdjustPolylineForRevolution( PolyLine& PL, const Point3d& ptAx, const Vector3d& plTrim.Translate( DIST_SIC * vtTrN) ; PL.Trim( plTrim, false) ; - // Se polilinea risultante è aperta con estremità molto vicine all'asse le porto su questo + // Se polilinea risultante � aperta con estremit� molto vicine all'asse le porto su questo if ( ! PL.IsClosed()) { // verifico l'inizio double dUStart ; @@ -2745,7 +2788,7 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve m_OGrMgr.Reset() ; ResetHashGrids3d() ; - // verifico se la polilinea è chiusa + // verifico se la polilinea � chiusa bool bClosed = MyPL.IsClosed() ; // costruisco la mesh @@ -2766,7 +2809,7 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve // verifico se il punto giace sull'asse e vi sta fisso bool bPrevOnAx = bOnlyRev && DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ; int nVPrevOnAx = nV ; - // se non è fisso sull'asse, inserisco le copie ruotate + // se non � fisso sull'asse, inserisco le copie ruotate if ( ! bPrevOnAx) { for ( int i = 1 ; i <= nStep ; ++i) { ptP.Rotate( ptAx, vtAx, dCosStepRot, dSinStepRot) ; @@ -2788,7 +2831,7 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve bool bOnAx = bOnlyRev && DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ; // ciclo sugli step for ( int i = 1 ; i <= nStep ; ++i) { - // se non è fisso sull'asse, inserisco le copie ruotate + // se non � fisso sull'asse, inserisco le copie ruotate if ( ! bOnAx) { ptP.Rotate( ptAx, vtAx, dCosStepRot, dSinStepRot) ; if ( ! bOnlyRev) @@ -2797,8 +2840,8 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve return false ; ++ nV ; } - // per i controlli già fatti non è possibile avere contemp. prec e corr su asse - // se il precedente è sull'asse, aggiungo un solo triangolo + // per i controlli gi� fatti non � possibile avere contemp. prec e corr su asse + // se il precedente � sull'asse, aggiungo un solo triangolo if ( bPrevOnAx) { nIdV[0] = nVPrevOnAx ; nIdV[1] = nV ; @@ -2806,7 +2849,7 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve if ( AddTriangle( nIdV) == SVT_NULL) return false ; } - // se il corrente è sull'asse, aggiungo un solo triangolo + // se il corrente � sull'asse, aggiungo un solo triangolo else if ( bOnAx) { nIdV[0] = nV - ( nStep + 2) + i ; nIdV[1] = nIdV[0] + 1 ; @@ -2826,8 +2869,8 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve } // se rivoluzione completa, aggiungo i due triangoli di chiusura if ( bFullRev) { - // per i controlli già fatti non è possibile avere contemp. prec e corr su asse - // se il precedente è sull'asse, aggiungo un solo triangolo + // per i controlli gi� fatti non � possibile avere contemp. prec e corr su asse + // se il precedente � sull'asse, aggiungo un solo triangolo if ( bPrevOnAx) { nIdV[0] = nVPrevOnAx ; nIdV[1] = nV - nStep ; @@ -2835,7 +2878,7 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve if ( AddTriangle( nIdV) == SVT_NULL) return false ; } - // se il corrente è sull'asse, aggiungo un solo triangolo + // se il corrente � sull'asse, aggiungo un solo triangolo else if ( bOnAx) { nIdV[0] = nV - 1 ; nIdV[1] = nV - ( nStep + 1) ; @@ -2858,7 +2901,7 @@ SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Ve // altrimenti ultimo punto di polilinea chiusa if ( bClosed) { for ( int i = 1 ; i <= nStep ; ++i) { - // non devo aggiungere i vertici, perchè coincidono con quelli iniziali + // non devo aggiungere i vertici, perch� coincidono con quelli iniziali // aggiungo triangolo in basso a sinistra nIdV[0] = nV - nStep + i - 1 ; nIdV[1] = nV - nStep + i ; nIdV[2] = i ; if ( AddTriangle( nIdV) == SVT_NULL) @@ -2894,7 +2937,7 @@ SurfTriMesh::AddBiTriangle( const int nIdVert[4]) // | | // 1 -> 2 int nIdV[3] ; - // se la diagonale 0->2 è uguale o più corta della 1->3 + // se la diagonale 0->2 � uguale o pi� corta della 1->3 if ( SqDist( m_vVert[nIdVert[0]].ptP, m_vVert[nIdVert[2]].ptP) <= SqDist( m_vVert[nIdVert[1]].ptP, m_vVert[nIdVert[3]].ptP) + EPS_SMALL) { // triangolo 0->1->2 @@ -2955,14 +2998,14 @@ SurfTriMesh::DoCompacting( double dTol) } // recupero la posizione geometrica del vertice Point3d ptP = m_vVert[nId].ptP ; - // se non c'è già un vertice con la stessa posizione lo inserisco nel grid + // se non c'� gi� un vertice con la stessa posizione lo inserisco nel grid int nAliasId ; if ( ! VertGrid.Find( ptP, dTol, nAliasId)) { VertGrid.InsertPoint( ptP, nId) ; // salvo l'Id nel vettore di reindirizzo vVId.push_back( nId) ; } - // c'è un vertice coincidente + // c'� un vertice coincidente else { // salvo l'Id alias nel vettore di reindirizzo vVId.push_back( nAliasId) ; @@ -2981,7 +3024,7 @@ SurfTriMesh::DoCompacting( double dTol) int vOId[3]{ m_vTria[nId].nIdVert[0], m_vTria[nId].nIdVert[1], m_vTria[nId].nIdVert[2]} ; - // verifico la validità degli indici + // verifico la validit� degli indici if ( vOId[0] < 0 || vOId[0] >= nVIdSize || vOId[1] < 0 || vOId[1] >= nVIdSize || vOId[2] < 0 || vOId[2] >= nVIdSize) @@ -2990,7 +3033,7 @@ SurfTriMesh::DoCompacting( double dTol) m_vTria[nId].nIdVert[0] = vVId[vOId[0]] ; m_vTria[nId].nIdVert[1] = vVId[vOId[1]] ; m_vTria[nId].nIdVert[2] = vVId[vOId[2]] ; - // se due vertici coincidono o la normale non è calcolabile, cancello il triangolo + // se due vertici coincidono o la normale non � calcolabile, cancello il triangolo if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[1] || m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[2] || m_vTria[nId].nIdVert[1] == m_vTria[nId].nIdVert[2] || @@ -3053,7 +3096,7 @@ SurfTriMesh::DoSewing( const ISurfTriMesh& stmOther, const Frame3d& frOther, dou Point3d ptOP = pOther->m_vVert[nOId].ptP ; // la porto nel riferimento della prima superficie ptOP.ToGlob( frOther) ; - // se non c'è già un vertice con la stessa posizione lo inserisco + // se non c'� gi� un vertice con la stessa posizione lo inserisco int nNewId ; if ( ! VertGrid.Find( ptOP, dTol, nNewId)) { if ( ( nNewId = AddVertex( ptOP)) == SVT_NULL) @@ -3075,7 +3118,7 @@ SurfTriMesh::DoSewing( const ISurfTriMesh& stmOther, const Frame3d& frOther, dou // salto i triangoli cancellati if ( vOId[0] == SVT_DEL) continue ; - // verifico la validità degli indici + // verifico la validit� degli indici if ( vOId[0] < 0 || vOId[0] >= nVIdSize || vOId[1] < 0 || vOId[1] >= nVIdSize || vOId[2] < 0 || vOId[2] >= nVIdSize) @@ -3127,7 +3170,7 @@ SurfTriMesh::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const // verifico lo stato if ( m_nStatus != OK) return false ; - // verifico validità del frame + // verifico validit� del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // assegno il box nel riferimento @@ -3172,7 +3215,7 @@ SurfTriMesh::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, if ( m_nStatus != OK) return false ; - // verifico validità dell'asse di rotazione + // verifico validit� dell'asse di rotazione if ( vtAx.IsSmall()) return false ; @@ -3238,7 +3281,7 @@ SurfTriMesh::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double bool bRecalc = ( abs( dCoeffX) < EPS_ZERO || abs( dCoeffY) < EPS_ZERO || abs( dCoeffZ) < EPS_ZERO) ; for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { if ( m_vTria[i].nIdVert[0] != SVT_DEL) { - // se c'è mirror, devo invertire la faccia + // se c'� mirror, devo invertire la faccia if ( bMirror) InvertTriangle( i) ; // aggiorno la normale @@ -3260,7 +3303,7 @@ SurfTriMesh::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double bool SurfTriMesh::InvertTriangle( int nT) { - // controllo validità triangolo + // controllo validit� triangolo if ( ! ExistsTriangle( nT)) return true ; // scambio di due vertici @@ -3276,10 +3319,10 @@ SurfTriMesh::InvertTriangle( int nT) bool SurfTriMesh::CalcTriangleNormal( int nT) { - // controllo validità triangolo + // controllo validit� triangolo if ( m_vTria[nT].nIdVert[0] == SVT_DEL) return true ; - // controllo validità vertici riferiti dal triangolo + // controllo validit� vertici riferiti dal triangolo if ( m_vTria[nT].nIdVert[0] < 0 || m_vTria[nT].nIdVert[0] >= GetVertexSize() || m_vVert[m_vTria[nT].nIdVert[0]].nIdTria == SVT_DEL || @@ -3308,7 +3351,7 @@ SurfTriMesh::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) // la superficie deve essere validata if ( m_nStatus != OK) return false ; - // verifico validità del piano di specchiatura + // verifico validit� del piano di specchiatura if ( vtNorm.IsSmall()) return false ; @@ -3340,7 +3383,7 @@ SurfTriMesh::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& // la superficie deve essere validata if ( m_nStatus != OK) return false ; - // verifico validità dei parametri + // verifico validit� dei parametri if ( vtNorm.IsSmall() || vtDir.IsSmall()) return false ; @@ -3371,11 +3414,11 @@ SurfTriMesh::ToGlob( const Frame3d& frRef) // la superficie deve essere validata if ( m_nStatus != OK) return false ; - // verifico validità del frame + // verifico validit� del frame if ( frRef.GetType() == Frame3d::ERR) return false ; - // se frame identità, non devo fare alcunché + // se frame identit�, non devo fare alcunch� if ( IsGlobFrame( frRef)) return true ; @@ -3405,11 +3448,11 @@ SurfTriMesh::ToLoc( const Frame3d& frRef) // la superficie deve essere validata if ( m_nStatus != OK) return false ; - // verifico validità del frame + // verifico validit� del frame if ( frRef.GetType() == Frame3d::ERR) return false ; - // se frame identità, non devo fare alcunché + // se frame identit�, non devo fare alcunch� if ( IsGlobFrame( frRef)) return true ; @@ -3439,11 +3482,11 @@ SurfTriMesh::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) // la superficie deve essere validata if ( m_nStatus != OK) return false ; - // verifico validità dei frame + // verifico validit� dei frame if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR) return false ; - // se i due riferimenti coincidono, non devo fare alcunché + // se i due riferimenti coincidono, non devo fare alcunch� if ( AreSameFrame( frOri, frDest)) return true ; @@ -3500,7 +3543,7 @@ SurfTriMesh::ResetHashGrids3d( void) const bool SurfTriMesh::VerifyHashGrids3d( void) const { - // se già calcolato, non devo fare altro + // se gi� calcolato, non devo fare altro if ( m_pHGrd3d != nullptr) return true ; // alloco @@ -3571,7 +3614,7 @@ SurfTriMesh::VerifyConnection( void) const // ciclo sui triangoli m_nParts = 0 ; for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) { - // salto triangoli cancellati o già assegnati + // salto triangoli cancellati o gi� assegnati if ( m_vTria[i].nIdVert[0] == SVT_DEL || m_vTria[i].nPart != SVT_NULL) continue ; diff --git a/SurfTriMesh.h b/SurfTriMesh.h index 8390d3a..e972ad2 100644 --- a/SurfTriMesh.h +++ b/SurfTriMesh.h @@ -27,15 +27,19 @@ class SurfFlatRegion ; // Classe Vertice class StmVert { - public : - StmVert( void) : ptP(), nIdTria( SVT_NULL), nFlag( 0), nTemp( 0) {} - StmVert( const Point3d& ptQ) : ptP( ptQ), nIdTria( SVT_NULL), nFlag( 0), nTemp( 0) {} - StmVert( const Point3d& ptQ, int nIdT, int nF) : ptP( ptQ), nIdTria( nIdT), nFlag( nF), nTemp( 0) {} - public : - Point3d ptP ; - int nIdTria ; - int nFlag ; - mutable int nTemp ; +public : + StmVert( void) : ptP(), dU( -1), dV( -1), nIdTria( SVT_NULL), nFlag( 0), nTemp( 0) {} + StmVert( const Point3d& ptQ) : ptP( ptQ), dU( -1), dV( -1), nIdTria( SVT_NULL), nFlag( 0), nTemp( 0) {} + StmVert( const Point3d& ptQ, int nIdT, int nF) : ptP( ptQ), dU( -1), dV( -1), nIdTria( nIdT), nFlag( nF), nTemp( 0) {} +public : + Point3d ptP ; + double dU ; // parametro riferito alle coordinate del punto nello spazio parametrico ( nSpanU x 1000) ( nSpanV x 1000) + // della sup di Bezier // -1 se non definito + double dV ; // parametro riferito alle coordinate del punto nello spazio parametrico ( nSpanU x 1000) ( nSpanV x 1000) + // della sup di Bezier // -1 se non definito + int nIdTria ; + int nFlag ; + mutable int nTemp ; } ; //---------------------------------------------------------------------------- @@ -215,7 +219,7 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW { m_dSmoothAng = std::max( dSmoothAngDeg, EPS_ANG_SMALL) ; m_dCosSmAng = cos( m_dSmoothAng * DEGTORAD) ; m_OGrMgr.Reset() ; } - int AddVertex( const Point3d& ptVert) override ; + int AddVertex( const Point3d& ptVert, const double dU = -1 , const double dV = -1) override ; bool MoveVertex( int nInd, const Point3d& ptNewVert) override ; int AddTriangle( const int nIdVert[3], int nTFlag = 0) override ; bool RemoveTriangle( int nId) override ; @@ -244,8 +248,11 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW double GetSmoothAngle( void) const override { return m_dSmoothAng ; } bool GetVertex( int nId, Point3d& ptP) const override ; + bool GetVertexParam( int nId, double& dU, double& dV) const override ; int GetFirstVertex( Point3d& ptP) const override ; + int GetFirstVertexParam( int nId, double& dU, double& dV) const override ; int GetNextVertex( int nId, Point3d& ptP) const override ; + int GetNextVertexParam( int nId, double& dU, double& dV) const override ; bool GetTriangle( int nId, int nIdVert[3]) const override ; int GetFirstTriangle( int nIdVert[3]) const override ; int GetNextTriangle( int nId, int nIdVert[3]) const override ; @@ -392,14 +399,14 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW double m_dCosBndAng ; // coseno dell'angolo limite per considerare un lato un contorno double m_dSmoothAng ; // angolo limite per mediare le normali (in gradi) double m_dCosSmAng ; // coseno dell'angolo limite per mediare le normali - bool m_bOriented ; // la superficie è orientata consistentemente in tutte le sue parti + bool m_bOriented ; // la superficie � orientata consistentemente in tutte le sue parti bool m_bClosed ; // la superficie racchiude un volume - bool m_bFaceted ; // flag di validità della sfaccettatura + bool m_bFaceted ; // flag di validit� della sfaccettatura VERTVECTOR m_vVert ; // vettore dei vertici TRIAVECTOR m_vTria ; // vettore dei triangoli INTVECTOR m_vFacet ; // vettore delle sfaccettature mutable int m_nTimeStamp ; // orologio locale - int m_nTempProp[2] ; // vettore proprietà temporanee + int m_nTempProp[2] ; // vettore propriet� temporanee int m_nMaxTFlag ; // massimo valore dei TFlag dei triangoli mutable int m_nParts ; // numero di parti connesse (-1 se da calcolare) mutable HashGrids3d* m_pHGrd3d ; // Hash Grid 3d nel suo riferimento diff --git a/Tree.cpp b/Tree.cpp new file mode 100644 index 0000000..116fc0c --- /dev/null +++ b/Tree.cpp @@ -0,0 +1,3554 @@ +//---------------------------------------------------------------------------- +// EgalTech 2023 +//---------------------------------------------------------------------------- +// File : Tree.cpp Data : 21.04.23 Versione : +// Contenuto : Implementazione della classe Tree. +// +// +// +// Modifiche : 21.04.23 DB Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include +#include "Tree.h" +#include "SurfBezier.h" +#include "GeoConst.h" +#include "CurveLine.h" +#include "CurveComposite.h" +#include "SurfFlatRegion.h" +#include "IntersLineLine.h" +#include "/EgtDev/Include/EGkPolyLine.h" +#include "/EgtDev/Include/EGkDistPointCurve.h" +#include "/EgtDev/Include/EGkCurve.h" +#include "DistPointCrvComposite.h" + +//---------------------------------------------------------------------------- +Cell::Cell( void) + : m_nId( -1),m_nTop ( -2), m_nBottom( -2), m_nLeft( -2), m_nRight ( -2), m_nParent( -2), m_nDepth( 0), + m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_nFlag2( 0), m_nRightEdgeIn( -1), m_bOnLeftEdge( false), m_bOnTopEdge( false), + m_ptPbl( ORIG), m_ptPtr(), m_bProcessed( false), m_bSplitVert( true) +{ + Point3d ptTr ( 1 * SBZ_TREG_COEFF, 1 * SBZ_TREG_COEFF) ; + m_ptPtr = ptTr ; +} + + +//---------------------------------------------------------------------------- +Cell::Cell( const Point3d& ptBL, const Point3d& ptTR) + : m_nId( -1),m_nTop ( -2), m_nBottom( -2), m_nLeft( -2), m_nRight ( -2), m_nParent( -2), m_nDepth( 0), + m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_nFlag2( 0), m_nRightEdgeIn( -1), m_bOnLeftEdge( false), m_bOnTopEdge( false), + m_ptPbl( ptBL), m_ptPtr( ptTR), m_bProcessed( false), m_bSplitVert( true) +{} + + +//---------------------------------------------------------------------------- +Cell::~Cell( void) +{ +} + +//---------------------------------------------------------------------------- +inline bool +Cell::IsSame( const Cell& cOtherCell) const +{ + if ( m_nId == cOtherCell.m_nId) + return true ; + else + return false ; +} + +//---------------------------------------------------------------------------- +bool +Cell::IsLeaf ( void) const +{ + if( m_nChild1 == -2 && m_nChild2 == -2) + return true ; + else + return false ; +} + +//---------------------------------------------------------------------------- +Tree::Tree( void) + : m_pSrfBz( nullptr), m_bTrimmed( false), m_bBilinear( false), m_bMulti( false), m_bClosedU( false), m_bClosedV( false), m_bSplitPatches( true) +{ + Point3d ptBl( 0, 0), ptTr ( 1 * SBZ_TREG_COEFF, 1 * SBZ_TREG_COEFF) ; + Cell cRoot( ptBl, ptTr) ; + m_mTree.insert( std::pair< int, Cell>( -1, cRoot)) ; +} + +//---------------------------------------------------------------------------- +Tree::Tree( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d ptMin, const Point3d ptMax) + : m_pSrfBz( nullptr), m_bTrimmed( false), m_bBilinear( false), m_bMulti( false), m_bClosedU( false), m_bClosedV( false), m_bSplitPatches( true) +{ + SetSurf( pSrfBz, bSplitPatches, ptMin, ptMax) ; +} + +//---------------------------------------------------------------------------- +Tree::~Tree( void) +{ +} + +//---------------------------------------------------------------------------- +void +Tree::SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d ptMin, const Point3d ptMax) +{ + // pulisco i vettori membri + m_vnLeaves.clear() ; + m_vnParents.clear() ; + m_mVert.clear() ; + m_vLoop.clear() ; + m_mChunk.clear() ; + m_vPlApprox.clear() ; + m_vChunk.clear() ; + m_vPolygons.clear() ; + + + + m_pSrfBz = pSrfBz ; + m_bSplitPatches = bSplitPatches ; + // le coordinate delle celle sono nello spazio parametrico + int nDegU, nDegV, nSpanU, nSpanV ; + bool bIsRat, bTrimmed ; + m_pSrfBz->GetInfo( nDegU, nDegV, nSpanU, nSpanV, bIsRat, bTrimmed) ; + m_bTrimmed = bTrimmed ; + m_nDegU = nDegU ; + m_nDegV = nDegV ; + m_nSpanU = nSpanU ; + m_nSpanV = nSpanV ; + if ( nDegU == 1 && nDegV == 1) + m_bBilinear = true ; + if ( nSpanU * nSpanV != 1) + m_bMulti = true ; + // recupero i loop di trim e li divido per chunk + if ( m_bTrimmed) { + int nLoop = 0 ; + INTVECTOR vChunk ; + + // recupero la superficie di trim per avere accesso diretto ai loop e mantenendo le informazioni sui chunk + PtrOwner pTrimReg( m_pSrfBz->GetTrimRegion()->Clone()) ; + double dLinTol = 0.01 ; // questo è riferito allo spazio parametrico + double dAngTolDeg = 5 ; + for ( int i = 0 ; i < pTrimReg->GetChunkCount() ; ++ i) { + PtrOwner pChunk( pTrimReg->CloneChunk( i)) ; + for ( int j = 0 ; j < pChunk->GetLoopCount( 0) ; ++ j) { + vChunk.push_back( nLoop) ; + // i chunk della falt region sono ancora flat region composte da 1 chunk + PtrOwner pLoop ( pChunk->GetLoop( 0, j)) ; + PtrOwner pCrv( pLoop->Clone()) ; + m_vLoop.emplace_back( Release( pLoop)) ; + m_mChunk[nLoop] = i ; + ++ nLoop ; + // approssimo i loop di trim con delle spezzate + PolyLine plApprox ; + int nType = 0 ; + pCrv->ApproxWithLines( dLinTol,dAngTolDeg, nType, plApprox) ; + // calcolo se il loop è CCW o CW + double dArea ; + Plane3d plExtPlane ; + bool bCCW ; + plApprox.IsClosedAndFlat( plExtPlane, dArea, 50 * EPS_SMALL) ; + if ( plExtPlane.GetVersN().z > 0) + bCCW = true ; + else + bCCW = false ; + m_vPlApprox.push_back( std::tuple(plApprox,bCCW)) ; + } + // li riordino dal più grande al più piccolo + std::sort(vChunk.begin(), vChunk.end(), [this]( const int& a, const int& b) { double dArea1, dArea2 ; + m_vLoop[a]->GetAreaXY( dArea1) ; + m_vLoop[b]->GetAreaXY( dArea2) ; + return dArea1 > dArea2 ;}) ; + m_vChunk.push_back( vChunk) ; + vChunk.clear() ; + } + // li riordino dal più grande al più piccolo + std::sort(m_vChunk.begin(), m_vChunk.end(), [this]( const INTVECTOR& a, const INTVECTOR& b) { double dArea1, dArea2 ; + m_vLoop[a[0]]->GetAreaXY( dArea1) ; + m_vLoop[b[0]]->GetAreaXY( dArea2) ; + return dArea1 > dArea2 ;}) ; + } + // salvo i vertici 3d della cella root + Point3d ptTop( nSpanU * SBZ_TREG_COEFF, nSpanV * SBZ_TREG_COEFF) ; + bool bLimited = false ; + if ( ! AreSamePointExact( ptMax,ORIG) && ! AreSamePointExact( ptMax,ptTop)) { + ptTop = ptMax ; + bLimited = true ; + } + m_mTree.clear() ; + Cell cRoot( ptMin, ptTop) ; + m_mTree.insert( std::pair< int, Cell>( -1, cRoot)) ; + Point3d ptP00, ptP10, ptP11, ptP01 ; + bool bOk = false ; + if ( ! bLimited) { + ptP00 = m_pSrfBz->GetControlPoint( 0, &bOk) ; + ptP10 = m_pSrfBz->GetControlPoint( nDegU * nSpanU, &bOk) ; + ptP11 = m_pSrfBz->GetControlPoint( ( nDegU * nSpanU + 1) * ( nDegV * nSpanV + 1) - 1, &bOk) ; + ptP01 = m_pSrfBz->GetControlPoint( ( nDegU * nSpanU + 1) * ( nDegV * nSpanV), &bOk) ; + } + else { + m_pSrfBz->GetPointD1D2( ptMin.x / SBZ_TREG_COEFF, ptMin.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00) ; + m_pSrfBz->GetPointD1D2( ptMax.x / SBZ_TREG_COEFF, ptMin.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10) ; + m_pSrfBz->GetPointD1D2( ptMax.x / SBZ_TREG_COEFF, ptMax.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11) ; + m_pSrfBz->GetPointD1D2( ptMin.x / SBZ_TREG_COEFF, ptMax.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01) ; + } + PNTVECTOR vVert ; + vVert.push_back( ptP00) ; + vVert.push_back( ptP10) ; + vVert.push_back( ptP11) ; + vVert.push_back( ptP01) ; + m_mVert.insert( std::pair( -1, vVert)) ; + // se richiesto divido preliminarmente le patches + m_vnParents.clear() ; + if ( m_bSplitPatches && ( nSpanU > 1 || nSpanV > 1)) { + int nId = -1 ; + // se la superficie è chiusa lungo il parametro U, sistemo le adiacenze al bordo + if ( AreSamePointApprox(ptP00, ptP10) && AreSamePointApprox(ptP01, ptP11) ) { + m_mTree[-1].m_nLeft = -1 ; + m_mTree[-1].m_nRight = -1 ; + m_bClosedU = true ; + } + // se la superficie è chiusa lungo il parametro V, sistemo le adiacenze al bordo + if ( ( AreSamePointApprox(ptP00, ptP01) && AreSamePointApprox(ptP10, ptP11) ) ) { + m_mTree[-1].m_nTop = -1 ; + m_mTree[-1].m_nBottom = -1 ; + m_bClosedV = true ; + } + for ( int i = 1 ; i < nSpanU ; ++i) { + if ( i * SBZ_TREG_COEFF > ptMin.x && i * SBZ_TREG_COEFF < ptTop.x){ + m_mTree[nId].SetSplitDirVert( true) ; + Split( nId, i * SBZ_TREG_COEFF) ; + ++ nId ; + ++ nId ; + } + } + INTVECTOR vLeaves ; + GetHeightLeaves( -1, vLeaves) ; + for ( int nId : vLeaves) { + for ( int j = nSpanV - 1 ; j > 0 ; --j) { + if ( j * SBZ_TREG_COEFF > ptMin.y && j * SBZ_TREG_COEFF < ptTop.y){ + m_mTree[nId].SetSplitDirVert( false) ; + Split( nId, j * SBZ_TREG_COEFF) ; + nId = m_mTree[nId].m_nChild2 ; + } + } + } + vLeaves.clear() ; + GetHeightLeaves( -1, vLeaves) ; + m_vnParents = vLeaves ; + } + // controllo se la superficie è chiusa. + // se è chiusa e non ho già fatto split preliminare, splitto sul parametro su cui è chiusa + // e sistemo le adiacenze + if ( ( AreSamePointApprox( ptP00, ptP01) || AreSamePointApprox( ptP10, ptP11)) || + ( AreSamePointApprox( ptP00, ptP10) || AreSamePointApprox( ptP01, ptP11))) { + //m_bClosed = true ; + if ( ( AreSamePointApprox( ptP00, ptP01) || AreSamePointApprox( ptP10, ptP11)) && (int) m_mTree.size() == 1) { + if ( AreSamePointApprox( ptP00, ptP01) && AreSamePointApprox( ptP10, ptP11)) { + m_mTree[-1].m_nTop = -1 ; + m_mTree[-1].m_nBottom = -1 ; + m_bClosedV = true ; + } + m_mTree[-1].SetSplitDirVert( false) ; + Split( -1) ; + // qui devo fare il controllo capped ( chiusura a semisfera) + // devo controllare se i punti ai parametri U=0 e U=1 sono tutti coincidenti + // in caso devo fare uno split nell'altra direzione + bool bOk = false ; + bool bCapped0 = true, bCapped1 = true ; + Point3d ptV0, ptV1 ; + // controllo se tutti i punti sull'isoparametrica sono uguali + for ( int i = 1 ; i < nDegV * nSpanV + 1 ; ++ i) { + ptV0 = m_pSrfBz->GetControlPoint( i * ( nDegU * nSpanU + 1), &bOk) ; + bCapped0 = bCapped0 && AreSamePointApprox( ptP00, ptV0) ; + ptV1 = m_pSrfBz->GetControlPoint( ( i + 1) * ( nDegU * nSpanU + 1) - 1, &bOk) ; + bCapped1 = bCapped1 && AreSamePointApprox( ptP10, ptV1) ; + } + if ( bCapped0 && bCapped1) { + m_mTree[0].SetSplitDirVert( true) ; + Split( 0) ; + m_mTree[1].SetSplitDirVert( true) ; + Split( 1) ; + } + } + // nella condizione di questo if non controllo eventuali divisioni preliminari, perché ne tengo conto dopo + if ( AreSamePointApprox( ptP00, ptP10) || AreSamePointApprox( ptP01, ptP11)) { + if( (int) m_mTree.size() == 1) { + if ( AreSamePointApprox( ptP00, ptP10) && AreSamePointApprox( ptP01, ptP11)) { + m_mTree[-1].m_nLeft = -1 ; + m_mTree[-1].m_nRight = -1 ; + m_bClosedU = true ; + } + m_mTree[-1].SetSplitDirVert( true) ; + Split( -1) ; + // devo controllare se i punti ai parametri V=0 e V=1 sono tutti coincidenti + // in caso devo fare uno split nell'altra direzione + bool bOk = false ; + bool bCapped0 = true, bCapped1 = true ; + Point3d ptU0, ptU1 ; + // controllo se tutti i punti sull'isoparametrica sono uguali + for ( int i = 1 ; i < nDegU * nSpanU + 1 ; ++ i) { + ptU0 = m_pSrfBz->GetControlPoint( i, &bOk) ; + bCapped0 = bCapped0 && AreSamePointApprox( ptP00, ptU0) ; + ptU1 = m_pSrfBz->GetControlPoint( i + ( nDegU * nSpanU + 1) * ( nDegV * nSpanV), &bOk) ; + bCapped1 = bCapped1 && AreSamePointApprox( ptP01, ptU1) ; + } + if ( bCapped0 && bCapped1) { + m_mTree[0].SetSplitDirVert( false) ; + Split( 0) ; + m_mTree[1].SetSplitDirVert( false) ; + Split( 1) ; + } + } + // se ho fatto solo 1 split e ho due celle foglie nId = 0 e nId = 1 + else if ( (int) m_mTree.size() > 1 && (int) m_mTree.size() < 4) { // si può mettere anche < 5 + m_mTree[0].m_nLeft = -1 ; + m_mTree[0].m_nRight = -1 ; + m_mTree[1].m_nLeft = -1 ; + m_mTree[1].m_nRight = -1 ; + m_mTree[0].SetSplitDirVert( true) ; + Split( 0) ; + m_mTree[1].SetSplitDirVert( true) ; + Split( 1) ; + } + } + } + + // calcolo e salvo la lunghezza reale delle curve di bezier di bordo + PtrOwner pCrvV0( m_pSrfBz->GetCurveOnU( 0)) ; + PtrOwner pCrvV1( m_pSrfBz->GetCurveOnU( double(nSpanV))) ; + PtrOwner pCrvU0( m_pSrfBz->GetCurveOnV( 0)) ; + PtrOwner pCrvU1( m_pSrfBz->GetCurveOnV( double(nSpanU))) ; + double dLen0 ; pCrvV0->GetApproxLength( dLen0) ; + double dLen1 ; pCrvU1->GetApproxLength( dLen1) ; + double dLen2 ; pCrvV1->GetApproxLength( dLen2) ; + double dLen3 ; pCrvU0->GetApproxLength( dLen3) ; + if ( dLen0 < EPS_ZERO && dLen2 < EPS_ZERO ) { + PtrOwner pCrvV( m_pSrfBz->GetCurveOnU( double(nSpanV) / 2)) ; + pCrvV->GetApproxLength( dLen0) ; + if ( dLen0 < EPS_ZERO ) { + pCrvV.Set( m_pSrfBz->GetCurveOnU( double(nSpanV) / 4)) ; + pCrvV->GetApproxLength( dLen0) ; + } + } + if ( dLen1 < EPS_ZERO && dLen3 < EPS_ZERO ) { + PtrOwner pCrvU( m_pSrfBz->GetCurveOnV( double(nSpanU) / 2)) ; + pCrvU->GetApproxLength( dLen1) ; + if ( dLen1 < EPS_ZERO ) { + pCrvU.Set( m_pSrfBz->GetCurveOnV( double(nSpanU) / 4)) ; + pCrvU->GetApproxLength( dLen1) ; + } + } + m_vDim.clear() ; + m_vDim.push_back( ( dLen0 > EPS_ZERO ? dLen0 : 1)) ; + m_vDim.push_back( ( dLen1 > EPS_ZERO ? dLen1 : 1)) ; + m_vDim.push_back( ( dLen2 > EPS_ZERO ? dLen2 : 1)) ; + m_vDim.push_back( ( dLen3 > EPS_ZERO ? dLen3 : 1)) ; +} + +//---------------------------------------------------------------------------- +bool +AddOrMergeBBox ( const BBox3d& bBox3dA , std::vector& vBBox, const bool& bAdd = true, const int& nInd = 0) { + Point3d ptMin = bBox3dA.GetMin() ; + Point3d ptMax = bBox3dA.GetMax() ; + if ( (int)vBBox.size() == 0) { + vBBox.push_back( bBox3dA) ; + return true ; + } + bool bAdded = false ; + for ( int b = 0 ; b < (int)vBBox.size() ; ++b) { + BBox3d bBox3dB = vBBox[b] ; + BBox3d b3Int ; + // se sono celle diverse e ho un'intersezione faccio il merge + if ( ! ( AreSamePointExact( ptMin, bBox3dB.GetMin()) && AreSamePointExact( ptMax, bBox3dB.GetMax())) && + bBox3dA.FindIntersectionXY( bBox3dB, b3Int)) { + vBBox[b].Add( bBox3dA) ; + if ( ! bAdd ) { + vBBox.erase( vBBox.begin() + nInd) ; + -- b ; + } + // se ho fatto un merge devo controllare se ora la nuova bbox ha delle intersezioni + AddOrMergeBBox( vBBox[b], vBBox, false, b) ; + bAdded = true ; + break ; + } + } + if ( ! bAdded && bAdd) + vBBox.push_back( bBox3dA) ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::GetIndependentTrees( std::vector>& vTrees) +{ + if ( ! m_bTrimmed ) { + vTrees.push_back( std::tuple( ORIG, ORIG)); + } + else { + // se ho dei loop di trim trovo le loro BBox3d per costruire l'albero solo all'interno di queste BBox + bool bIsRoot = false ; + std::vector vBBox ; + for ( int a = 0 ; a < (int)m_vPlApprox.size(); ++a ) { + PolyLine plLoop = std::get<0>( m_vPlApprox[a]) ; + // calcolo la BBox3d + BBox3d bBox3dA ; + Point3d pt ; + plLoop.GetFirstPoint( pt) ; + bBox3dA.Add( pt) ; + while ( plLoop.GetNextPoint( pt)) + bBox3dA.Add( pt) ; + // controllo se ho intersezioni con altre bbox + // se ho intersezioni unisco le bbox, altrimenti le lascio indipendenti + AddOrMergeBBox( bBox3dA, vBBox) ; + } + // controllo se dopo aver unito le bbox ho ottenuto Root + Point3d ptTR( m_nSpanU * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF) ; + for (int c = 0 ; c < (int)vBBox.size(); ++ c) { + BBox3d bBox3d = vBBox[c] ; + if ( AreSamePointEpsilon( bBox3d.GetMin(), ORIG, 10) && AreSamePointEpsilon( bBox3d.GetMax(), ptTR, 10)){ + bIsRoot = true ; + break ; + } + } + // restituisco le celle parent di partenza a partire dalle bbox che ho ottenuto + if ( ! bIsRoot ) { + for (int i = 0 ; i < (int)vBBox.size() ; ++ i) { + Point3d ptMin = vBBox[i].GetMin() ; + Point3d ptMax = vBBox[i].GetMax() ; + vTrees.push_back( std::tuple( ptMin, ptMax)) ; + } + } + else + vTrees.push_back( std::tuple( ORIG, ORIG)); + } + return true ; +} + +//---------------------------------------------------------------------------- +void +Tree::Split( const int& nId, const double& dSplitValue) +{ + // controllo che lo split non venga fatto sul lato della cella + if ( ( m_mTree[nId].IsSplitVert() && dSplitValue > m_mTree[nId].GetBottomLeft().x + EPS_SMALL && dSplitValue < m_mTree[nId].GetTopRight().x - EPS_SMALL) || + ( ! m_mTree[nId].IsSplitVert() && dSplitValue > m_mTree[nId].GetBottomLeft().y + EPS_SMALL && dSplitValue < m_mTree[nId].GetTopRight().y - EPS_SMALL)) { + // quando si implementerà lo split a parametro libero bisognerà impedire che si facciano split troppo vicini al bordo della cella!!!!!!!!!!!!!!!!!!! + m_mTree[nId].m_dSplit = dSplitValue ; + Cell cChild1, cChild2 ; + cChild1.m_nDepth = m_mTree[nId].m_nDepth + 1 ; + cChild2.m_nDepth = m_mTree[nId].m_nDepth + 1 ; + int nNodes = (int) m_mTree.size() ; + cChild1.m_nId = nNodes - 1 ; + m_mTree[nId].m_nChild1 = nNodes - 1 ; + cChild2.m_nId = nNodes ; + m_mTree[nId].m_nChild2 = nNodes ; + m_mTree.insert( std::pair( nNodes - 1, cChild1)) ; + m_mTree.insert( std::pair( nNodes, cChild2)) ; + Point3d ptVert1, ptVert2 ; + PNTVECTOR vVert ; + m_mVert.insert( std::pair( nNodes - 1, vVert)) ; + m_mVert.insert( std::pair( nNodes, vVert)) ; + if ( ! m_mTree[nId].IsSplitVert()) + { + // la cella figlio 1 è quella sopra + Point3d ptBL( m_mTree[nId].GetBottomLeft().x, dSplitValue) ; + m_mTree[m_mTree[nId].m_nChild1].SetBottomLeft( ptBL) ; + m_mTree[m_mTree[nId].m_nChild1].SetTopRight( m_mTree[nId].GetTopRight()) ; + m_mTree[m_mTree[nId].m_nChild1].m_nTop = m_mTree[nId].m_nTop ; + m_mTree[m_mTree[nId].m_nChild1].m_nBottom = m_mTree[nId].m_nChild2 ; + m_mTree[m_mTree[nId].m_nChild1].m_nLeft = m_mTree[nId].m_nLeft ; + m_mTree[m_mTree[nId].m_nChild1].m_nRight = m_mTree[nId].m_nRight ; + Point3d ptTR( m_mTree[nId].GetTopRight().x, dSplitValue) ; + m_mTree[m_mTree[nId].m_nChild2].SetBottomLeft( m_mTree[nId].GetBottomLeft()) ; + m_mTree[m_mTree[nId].m_nChild2].SetTopRight( ptTR) ; + m_mTree[m_mTree[nId].m_nChild2].m_nTop = m_mTree[nId].m_nChild1 ; + m_mTree[m_mTree[nId].m_nChild2].m_nBottom = m_mTree[nId].m_nBottom ; + m_mTree[m_mTree[nId].m_nChild2].m_nLeft = m_mTree[nId].m_nLeft ; + m_mTree[m_mTree[nId].m_nChild2].m_nRight = m_mTree[nId].m_nRight ; + // metto i corrispondenti 3d dei punti dello split nella mappa m_mVert + // per ogni cella i punti devono essere nell'ordine ptP00, ptP10, ptP11, ptP01 + m_pSrfBz->GetPointD1D2( m_mTree[nId].GetBottomLeft().x / SBZ_TREG_COEFF, dSplitValue / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptVert1) ; + m_pSrfBz->GetPointD1D2( m_mTree[nId].GetTopRight().x / SBZ_TREG_COEFF, dSplitValue / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptVert2) ; + m_mVert[nNodes - 1].push_back( ptVert1) ; + m_mVert[nNodes - 1].push_back( ptVert2) ; + m_mVert[nNodes - 1].push_back( m_mVert[nId][2]) ; + m_mVert[nNodes - 1].push_back( m_mVert[nId][3]) ; + m_mVert[nNodes].push_back( m_mVert[nId][0]) ; + m_mVert[nNodes].push_back( m_mVert[nId][1]) ; + m_mVert[nNodes].push_back( ptVert2) ; + m_mVert[nNodes].push_back( ptVert1) ; + } + else { + // la cella figlio 1 è quella di sinistra + Point3d ptTR( dSplitValue, m_mTree[nId].GetTopRight().y) ; + m_mTree[m_mTree[nId].m_nChild1].SetBottomLeft( m_mTree[nId].GetBottomLeft()) ; + m_mTree[m_mTree[nId].m_nChild1].SetTopRight( ptTR) ; + m_mTree[m_mTree[nId].m_nChild1].m_nTop = m_mTree[nId].m_nTop ; + m_mTree[m_mTree[nId].m_nChild1].m_nBottom = m_mTree[nId].m_nBottom ; + m_mTree[m_mTree[nId].m_nChild1].m_nLeft = m_mTree[nId].m_nLeft ; + m_mTree[m_mTree[nId].m_nChild1].m_nRight = m_mTree[nId].m_nChild2 ; + Point3d ptBL( dSplitValue, m_mTree[nId].GetBottomLeft().y) ; + m_mTree[m_mTree[nId].m_nChild2].SetBottomLeft( ptBL) ; + m_mTree[m_mTree[nId].m_nChild2].SetTopRight( m_mTree[nId].GetTopRight()) ; + m_mTree[m_mTree[nId].m_nChild2].m_nTop = m_mTree[nId].m_nTop ; + m_mTree[m_mTree[nId].m_nChild2].m_nBottom = m_mTree[nId].m_nBottom ; + m_mTree[m_mTree[nId].m_nChild2].m_nLeft = m_mTree[nId].m_nChild1 ; + m_mTree[m_mTree[nId].m_nChild2].m_nRight = m_mTree[nId].m_nRight ; + // metto i corrispondenti 3d dei punti dello split nella mappa m_mVert + // per ogni cella i punti devono essere nell'ordine ptP00, ptP10, ptP11, ptP01 + m_pSrfBz->GetPointD1D2( dSplitValue / SBZ_TREG_COEFF, m_mTree[nId].GetBottomLeft().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptVert2) ; + m_pSrfBz->GetPointD1D2( dSplitValue / SBZ_TREG_COEFF, m_mTree[nId].GetTopRight().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptVert1) ; + m_mVert[nNodes - 1].push_back( m_mVert[nId][0]) ; + m_mVert[nNodes - 1].push_back( ptVert2) ; + m_mVert[nNodes - 1].push_back( ptVert1) ; + m_mVert[nNodes - 1].push_back( m_mVert[nId][3]) ; + m_mVert[nNodes].push_back( ptVert2) ; + m_mVert[nNodes].push_back( m_mVert[nId][1]) ; + m_mVert[nNodes].push_back( m_mVert[nId][2]) ; + m_mVert[nNodes].push_back( ptVert1) ; + } + m_mTree[m_mTree[nId].m_nChild1].SetParent( nId) ; + m_mTree[m_mTree[nId].m_nChild2].SetParent( nId) ; + } +} + +//---------------------------------------------------------------------------- +void +Tree::Split( const int& nId) +{ + double dValue ; + if ( m_mTree[nId].IsSplitVert()) + dValue = ( m_mTree[nId].GetBottomLeft().x + m_mTree[nId].GetTopRight().x) / 2 ; + else + dValue = ( m_mTree[nId].GetBottomLeft().y + m_mTree[nId].GetTopRight().y) / 2 ; + Split( nId, dValue) ; +} + +//---------------------------------------------------------------------------- +bool +Tree::BuildTree_test( const double& dLinTol, const double& dSideMin, const double& dSideMax) +{ + // per poter usare questa funzione, anziché quella normale, bisogna: + // - commentare la parte di funzione di SetSurf dove si fanno gli split preliminare + // - se si usa anche la funzione GetLeaves, bisogna anche lì usare BuildTree_test al posto di BuildTree + + + //int nCToSplit = -1 ; + //celle 0,1 + m_mTree[-1].SetSplitDirVert( true) ; + Split( -1) ; + //celle 2,3 + m_mTree[0].SetSplitDirVert( false) ; + Split( 0) ; + //celle 4,5 + m_mTree[2].SetSplitDirVert( false) ; + Split( 2) ; + //celle 6,7 + m_mTree[3].SetSplitDirVert( true) ; + Split( 3) ; + //celle 8,9 + m_mTree[1].SetSplitDirVert( false) ; + Split( 1) ; + //celle 10,11 + m_mTree[8].SetSplitDirVert( true) ; + Split( 8) ; + //celle 12,13 + m_mTree[9].SetSplitDirVert( false) ; + Split( 9) ; + m_vnLeaves.push_back( 4) ; + //m_vnLeaves.push_back( 5) ; + m_vnLeaves.push_back( 6) ; + //m_vnLeaves.push_back( 7) ; + //m_vnLeaves.push_back( 10) ; + m_vnLeaves.push_back( 11) ; + //m_vnLeaves.push_back( 12) ; + m_vnLeaves.push_back( 13) ; + + // aggiunta di split + //celle 14,15 + m_mTree[5].SetSplitDirVert( true) ; + Split( 5) ; + m_vnLeaves.push_back( 14) ; + m_vnLeaves.push_back( 15) ; + //celle 16,17 + m_mTree[7].SetSplitDirVert( false) ; + Split( 7) ; + m_vnLeaves.push_back( 16) ; + m_vnLeaves.push_back( 17) ; + //celle 18,19 + m_mTree[12].SetSplitDirVert( true) ; + Split( 12) ; + m_vnLeaves.push_back( 18) ; + m_vnLeaves.push_back( 19) ; + //celle 20,21 + m_mTree[10].SetSplitDirVert( false) ; + Split( 10) ; + m_vnLeaves.push_back( 20) ; + m_vnLeaves.push_back( 21) ; + + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::BuildTree( const double& dLinTol, const double& dSideMin, const double& dSideMax) +{ + // suddivido lo spazio parametrico con divisioni a metà su uno dei due parametri + int nCToSplit = -1 ; + if ( ! m_bBilinear) { + while ( nCToSplit != -2 && m_mTree[nCToSplit].IsProcessed() == false) { + // controllo che la cella non sia già stata preliminarmente splittata + if ( m_mTree[nCToSplit].IsLeaf()) { + // calcolo in quale direzione ho più curvatura + // ptP00P10 è un punto tra P00 e P10 + double dCurvU = 0, dCurvV = 0 ; + double dLenParU = ( m_mTree[nCToSplit].GetTopRight().x - m_mTree[nCToSplit].GetBottomLeft().x) / SBZ_TREG_COEFF ; + double dLenParV = ( m_mTree[nCToSplit].GetTopRight().y - m_mTree[nCToSplit].GetBottomLeft().y) / SBZ_TREG_COEFF ; + if ( dLenParU <= 1. / m_nDegV || dLenParV <= 1. / m_nDegU) { + double dU = ( m_mTree[nCToSplit].GetTopRight().x + m_mTree[nCToSplit].GetBottomLeft().x) / 2 / SBZ_TREG_COEFF ; + double dV = ( m_mTree[nCToSplit].GetTopRight().y + m_mTree[nCToSplit].GetBottomLeft().y) / 2 / SBZ_TREG_COEFF ; + double dULoc = 0.5, dVLoc = 0.5 ; + Point3d ptPSrf, ptP00P10, ptP10P11, ptP11P01, ptP01P00 ; + m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; + m_pSrfBz->GetPointD1D2( dU, m_mTree[nCToSplit].GetBottomLeft().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00P10) ; + m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x / SBZ_TREG_COEFF, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10P11) ; + m_pSrfBz->GetPointD1D2( dU, m_mTree[nCToSplit].GetTopRight().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11P01) ; + m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x / SBZ_TREG_COEFF, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01P00) ; + Point3d ptV = ( 1 - dULoc) * ptP00P10 + dULoc * ptP11P01 ; + Point3d ptU = ( 1 - dVLoc) * ptP10P11 + dVLoc * ptP01P00 ; + dCurvV = Dist( ptV, ptPSrf) ; + dCurvU = Dist( ptU, ptPSrf) ; + } + // faccio un'analisi più fine della curvatura se almeno il grado di una curva di uno dei due parametri è alto e + // se sto ancora guardando una cella abbastanza grande + else{ + Point3d ptPSrf, ptP00P10, ptP10P11, ptP11P01, ptP01P00, ptPSrfMid ; + double dStep = 1. / m_nDegU ; + for ( double k = dStep ; k < 1 ; k = k + dStep) { + double dU = ( k * m_mTree[nCToSplit].GetTopRight().x + ( 1 - k) * m_mTree[nCToSplit].GetBottomLeft().x) / SBZ_TREG_COEFF ; + double dV = ( m_mTree[nCToSplit].GetTopRight().y + m_mTree[nCToSplit].GetBottomLeft().y) / 2 / SBZ_TREG_COEFF ; + m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; + if ( k == 0.5) + ptPSrfMid = ptPSrf ; + m_pSrfBz->GetPointD1D2( dU, m_mTree[nCToSplit].GetBottomLeft().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00P10) ; + m_pSrfBz->GetPointD1D2( dU, m_mTree[nCToSplit].GetTopRight().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11P01) ; + CurveLine clV ; + clV.Set( ptP00P10, ptP11P01) ; + DistPointCurve dpc( ptPSrf, clV) ; + double dDist ; + dpc.GetDist( dDist) ; + dCurvV = std::max( dCurvV, dDist) ; + } + dStep = 1. / m_nDegV ; + for ( double k = dStep ; k < 1 ; k = k + dStep) { + double dU = ( m_mTree[nCToSplit].GetTopRight().x + m_mTree[nCToSplit].GetBottomLeft().x) / 2 / SBZ_TREG_COEFF ; + double dV = ( k * m_mTree[nCToSplit].GetTopRight().y + ( 1 - k) * m_mTree[nCToSplit].GetBottomLeft().y) / SBZ_TREG_COEFF ; + if ( k == 0.5) + ptPSrf = ptPSrfMid ; + else + m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; + m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x / SBZ_TREG_COEFF, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10P11) ; + m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x / SBZ_TREG_COEFF, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01P00) ; + CurveLine clU ; + clU.Set( ptP01P00, ptP10P11) ; + DistPointCurve dpc( ptPSrf, clU) ; + double dDist ; + dpc.GetDist( dDist) ; + dCurvU = std::max( dCurvU, dDist) ; + } + } + + // per lo split scelgo la direzione che è più vicina alla superficie originale nel punto di maggior distanza + // misura approssimativa della curvatura in una direzione + bool bVert ; + if ( dCurvV > dCurvU) { + // lungo la direzione V ho una curvatura maggiore + bVert = false ; + } + else { + // lungo la direzione U ho una curvatura maggiore + bVert = true ; + } + m_mTree[nCToSplit].SetSplitDirVert( bVert) ; + Point3d ptP00, ptP10, ptP11, ptP01 ; + // distanza reale tra i vertici della cella + ptP00 = m_mVert[nCToSplit][0] ; + ptP10 = m_mVert[nCToSplit][1] ; + ptP11 = m_mVert[nCToSplit][2] ; + ptP01 = m_mVert[nCToSplit][3] ; + double dLen0 = Dist( ptP00, ptP10) ; + double dLen1 = Dist( ptP10, ptP11) ; + double dLen2 = Dist( ptP01, ptP11) ; + double dLen3 = Dist( ptP00, ptP01) ; + if ( dLen0 < EPS_ZERO && dLen2 < EPS_ZERO ) { + double dV = ( m_mTree[nCToSplit].GetBottomLeft().y + m_mTree[nCToSplit].GetTopRight().y) / 2 / SBZ_TREG_COEFF ; + PtrOwner pCrvV( m_pSrfBz->GetCurveOnU( dV)) ; + double dLenU0, dLenU1 ; + pCrvV->GetLengthAtParam( m_mTree[nCToSplit].GetBottomLeft().x / SBZ_TREG_COEFF, dLenU0) ; + pCrvV->GetLengthAtParam( m_mTree[nCToSplit].GetTopRight().x / SBZ_TREG_COEFF, dLenU1) ; + dLen0 = abs( dLenU1 - dLenU0) ; + } + if ( dLen1 < EPS_ZERO && dLen3 < EPS_ZERO ) { + double dU = ( m_mTree[nCToSplit].GetBottomLeft().x + m_mTree[nCToSplit].GetTopRight().x) / 2 / SBZ_TREG_COEFF ; + PtrOwner pCrvU( m_pSrfBz->GetCurveOnV( dU)) ; + double dLenV0, dLenV1 ; + pCrvU->GetLengthAtParam( m_mTree[nCToSplit].GetBottomLeft().y / SBZ_TREG_COEFF, dLenV0) ; + pCrvU->GetLengthAtParam( m_mTree[nCToSplit].GetTopRight().y / SBZ_TREG_COEFF, dLenV1) ; + dLen1 = abs( dLenV1 - dLenV0) ; + } + // verifico che la cella sia da splittare e che eventualmente sia abbastanza grande da poterlo fare + double dSideMinVal = 0, dSideMaxVal = 0 ; + if ( bVert) { + if ( dLen0 > EPS_ZERO && dLen2 > EPS_ZERO) + dSideMinVal = std::min( dLen0, dLen2) ; + else + dSideMinVal = std::max( dLen0, dLen2) ; + } + else { + if ( dLen1 > EPS_ZERO && dLen3 > EPS_ZERO) + dSideMinVal = std::min( dLen1, dLen3) ; + else + dSideMinVal = std::max( dLen1, dLen3) ; + } + // calcolo le diagonali per controllare la dimensione massima dei triangoli in cui dividerei la cella + dSideMaxVal = std::max( Dist( ptP00, ptP11), Dist( ptP10, ptP01)) ; + + // se la cella è abbastanza grande da poter essere divisa ancora, calcolo l'errore di approssimazione + bool bSplit = false ; + // dSideMinVal potrebbe essere zero se entrambi i lati che dovrei splittare sono collassati in un punto, ma questo non vuol + // dire che non dovrei eseguire lo split + if ( (dSideMinVal / 2 >= dSideMin || dSideMinVal < EPS_SMALL) && dSideMaxVal < dSideMax && ( dCurvV > dLinTol || dCurvU > dLinTol)) { + CurveLine cl0010, cl0001, cl1011, cl0111 ; + // V=0 + cl0010.Set( ptP00, ptP10) ; + // V=1 + cl0111.Set( ptP01, ptP11) ; + Point3d pt0010, pt0111, ptBz0, ptBz1, ptBzV ; + int nFlag ; + CurveLine clV ; + // determino quanti Step fare per ogni direzione parametrica + double dDimU = ( dLen0 >= dLen2 ? dLen0 / m_vDim[0] : dLen2 / m_vDim[2]) ; + dDimU = ( dDimU > 1 ? 1 : dDimU) ; + double dDimV = ( dLen1 >= dLen3 ? dLen1 / m_vDim[1] : dLen3 / m_vDim[3]) ; + dDimV = ( dDimV > 1 ? 1 : dDimV) ; + // numero di Step per campionare la superficie nelle due direzioni parametriche + int nStepsU = int( 51 * dDimU + 5 * ( 1 - dDimU)) ; + int nStepsV = int( 51 * dDimV + 5 * ( 1 - dDimV)) ; + for ( int u = 0 ; u < nStepsU && ! bSplit ; ++ u) { + double dU = double ( u) / double ( nStepsU - 1) ; + double dULoc = ( ( 1 - dU) * m_mTree[nCToSplit].GetBottomLeft().x + dU * m_mTree[nCToSplit].GetTopRight().x) / SBZ_TREG_COEFF ; + if ( ! m_pSrfBz->GetPointD1D2( dULoc, m_mTree[nCToSplit].GetBottomLeft().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz0) || + ! m_pSrfBz->GetPointD1D2( dULoc, m_mTree[nCToSplit].GetTopRight().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz1)) + return false ; + // verifico che la cella non sia uno spicchio in verticale, cioè con ptP00 == ptP01 && ptP10 == ptP11 + // ( vedi disegno sotto per uno spicchio verticale) + // sennò i punti che cerco sono semplicemente i vertici + if ( cl0010.IsValid()) { + DistPointCurve dpc0010( ptBz0, cl0010) ; + dpc0010.GetMinDistPoint( 0, pt0010, nFlag) ; + } + else + pt0010 = ptP00 ; + if ( cl0111.IsValid()) { + DistPointCurve dpc0111( ptBz1, cl0111) ; + dpc0111.GetMinDistPoint( 0, pt0111, nFlag) ; + } + else + pt0111 = ptP01 ; + // curva a parametro U fisso, con V che scorre + clV.Set( pt0010, pt0111) ; + for ( int v = 0 ; v < nStepsV ; ++ v) { + double dV = double ( v) / double ( nStepsV - 1) ; + double dVLoc = ( ( 1 - dV) * m_mTree[nCToSplit].GetBottomLeft().y + dV * m_mTree[nCToSplit].GetTopRight().y) / SBZ_TREG_COEFF ; + if ( ! m_pSrfBz->GetPointD1D2( dULoc, dVLoc, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBzV)) + return false ; + DistPointCurve dpc( ptBzV, clV) ; + // distanza di approssimazione locale + double dDist ; + dpc.GetDist( dDist) ; + // se la cella è uno spicchio, quindi con due lati collassati, devo calcolare in modo diverso dist + // ptP00 == ptP01 + // / \ + // / \ + // / \ + // ( ) + // \ / + // \ / + // \ / + // ptP10 == ptP11 + if ( ! clV.IsValid() && AreSamePointApprox( ptP00, ptP01) && AreSamePointApprox( ptP10, ptP11)) { + DistPointCurve dpcSlice( ptBzV, cl0010) ; + dpcSlice.GetDist( dDist) ; + } + if ( dDist > dLinTol) { + bSplit = true ; + break ; + } + } + } + } + + if ( bSplit || dSideMaxVal > dSideMax) { + m_mTree[nCToSplit].SetSplitDirVert( bVert) ; + // effettuo lo split + Split( nCToSplit) ; + + // procedo con lo split del Child1 + nCToSplit = m_mTree[nCToSplit].m_nChild1 ; + } + else { + // sono arrivato ad una cella Leaf, quindi salvo la cella + m_vnLeaves.push_back( nCToSplit) ; + m_mTree[nCToSplit].SetProcessed() ; + // risalgo i parent finché non trovo il primo Child2 da processare + nCToSplit = m_mTree[nCToSplit].m_nParent ; + if ( nCToSplit == -2) + return true ; + if ( m_mTree[m_mTree[nCToSplit].m_nChild1].IsProcessed() && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) + m_mTree[nCToSplit].SetProcessed() ; + while ( m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) { + if ( m_mTree[nCToSplit].m_nParent != -2) + nCToSplit = m_mTree[nCToSplit].m_nParent ; + if ( m_mTree[m_mTree[nCToSplit].m_nChild1].IsProcessed() && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) + m_mTree[nCToSplit].SetProcessed() ; + if ( nCToSplit == -1 && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) + break ; + } + nCToSplit = m_mTree[nCToSplit].m_nChild2 ; + } + } + else { + nCToSplit = m_mTree[nCToSplit].m_nChild1 ; + } + } + Balance() ; // da implementare quando dividerò ad un parametro a scelta e non a metà + } + // bilineare + else { + while ( nCToSplit != -2 && m_mTree[nCToSplit].IsProcessed() == false) { + if ( m_mTree[nCToSplit].IsLeaf()) { + // vertici della cella + Point3d ptP00, ptP10, ptP11, ptP01 ; + ptP00 = m_mVert[nCToSplit][0] ; + ptP10 = m_mVert[nCToSplit][1] ; + ptP11 = m_mVert[nCToSplit][2] ; + ptP01 = m_mVert[nCToSplit][3] ; + // distanza reale tra i vertici della cella + double dLen0 = Dist( ptP00, ptP10) ; + double dLen1 = Dist( ptP10, ptP11) ; + double dLen2 = Dist( ptP01, ptP11) ; + double dLen3 = Dist( ptP00, ptP01) ; + + bool bVert = false ; + // calcolo in quale direzione è meglio dividere in base allo stretch + Point3d ptPSrfU, ptPSrfV ; + double dU = 0, dV = 0 ; + double dDistU = 0, dDistV = 0 ; + PNTVECTOR vPtU, vPtV ; + if ( ! m_bMulti) { + if ( std::max( dLen0, dLen2) > std::max( dLen1, dLen3)) { + bVert = true ; + } + else { + bVert = false ; + } + } + else { + for ( double i = 0.25 ; i < 1 ; i = i + 0.25) { + dU = ( ( 1 - i) * m_mTree[nCToSplit].GetBottomLeft().x + i * m_mTree[nCToSplit].GetTopRight().x) / SBZ_TREG_COEFF ; + dV = ( ( 1 - i) * m_mTree[nCToSplit].GetBottomLeft().y + i * m_mTree[nCToSplit].GetTopRight().y) / SBZ_TREG_COEFF ; + double dVLoc = ( m_mTree[nCToSplit].GetBottomLeft().y + m_mTree[nCToSplit].GetTopRight().y) / 2 / SBZ_TREG_COEFF ; + double dULoc = ( m_mTree[nCToSplit].GetBottomLeft().x + m_mTree[nCToSplit].GetTopRight().x) / 2 / SBZ_TREG_COEFF ; + m_pSrfBz->GetPointD1D2( dU, dVLoc, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrfU) ; + m_pSrfBz->GetPointD1D2( dULoc, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrfV) ; + vPtU.push_back( ptPSrfU) ; + vPtV.push_back( ptPSrfV) ; + } + // devo guardare se i tre punti in vPtU e vPtV sono allineati + CurveLine clU, clV ; + clU.Set( vPtU[0], vPtU[1]) ; + clV.Set( vPtV[0], vPtV[1]) ; + DistPointCurve dpcU( vPtU[2], clU, false) ; + DistPointCurve dpcV( vPtV[2], clV, false) ; + dpcU.GetDist( dDistU) ; + dpcV.GetDist( dDistV) ; + if ( dDistU > dDistV) { + bVert = true ; + } + else { + bVert = false ; + } + } + + // verifico che la cella sia abbastanza grande da poter essere splittata + double dSideMinVal = 0, dSideMaxVal = 0 ; + if ( bVert) { + if ( dLen0 != 0 && dLen2 != 0) + dSideMinVal = std::max( dLen0, dLen2) ; + else + dSideMinVal = std::max( dLen0, dLen2) ; + } + else { + if ( dLen1 != 0 && dLen3 != 0) + dSideMinVal = std::max( dLen1, dLen3) ; + else + dSideMinVal = std::max( dLen1, dLen3) ; + } + // calcolo le diagonali per controllare la dimensione massima dei triangoli in cui dividerei la cella + dSideMaxVal = std::max( Dist( ptP00, ptP11), Dist( ptP10, ptP01)) ; + + + double dErr = 0 ; + if ( m_bMulti) { + Point3d ptPSrf ; + Plane3d plAppr ; + if ( ! AreSamePointApprox( ptP00, ptP10) && ! AreSamePointApprox( ptP00, ptP01)) + plAppr.Set( ptP00, ( ptP00 - ptP01) ^ ( ptP00 - ptP10)) ; + else if ( AreSamePointApprox( ptP00, ptP10)) { + plAppr.Set( ptP01, ( ptP00 - ptP01) ^ ( ptP01 - ptP11)) ; + } + else if ( AreSamePointApprox( ptP00, ptP01)) { + plAppr.Set( ptP10, ( ptP10 - ptP11) ^ ( ptP00 - ptP10)) ; + } + for ( double i = 0.25 ; i < 1 ; i = i + 0.25) { + for ( double j = 0.25 ; j < 1 ; j = j + 0.25) { + double dU = ( ( 1 - i) * m_mTree[nCToSplit].GetTopRight().x + i * m_mTree[nCToSplit].GetBottomLeft().x) / SBZ_TREG_COEFF ; + double dV = ( ( 1 - j) * m_mTree[nCToSplit].GetTopRight().y + j * m_mTree[nCToSplit].GetBottomLeft().y) / SBZ_TREG_COEFF ; + m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; + dErr = std::max( abs( DistPointPlane( ptPSrf, plAppr)), dErr) ; + } + } + } + else { + dErr = 1. / 4. * ( ( ptP00 - ptP01) + ( ptP11 - ptP10)).Len() ; + } + // se la cella è abbastanza grande da poter essere divisa ancora e devo approssimare meglio, la divido + if ( dSideMinVal / 2 >= dSideMin && dSideMaxVal < dSideMax && dErr > dLinTol) { + m_mTree[nCToSplit].SetSplitDirVert( bVert) ; + // effettuo lo split + Split( nCToSplit) ; + + // procedo con lo split del Child1 + nCToSplit = m_mTree[nCToSplit].m_nChild1 ; + } + else { + // sono arrivato ad una cella Leaf, quindi salvo la cella + m_vnLeaves.push_back( nCToSplit) ; + m_mTree[nCToSplit].SetProcessed() ; + // risalgo i parent finché non trovo il primo Child2 da processare + nCToSplit = m_mTree[nCToSplit].m_nParent ; + if ( nCToSplit == -2) + return true ; + if ( m_mTree[m_mTree[nCToSplit].m_nChild1].IsProcessed() && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) + m_mTree[nCToSplit].SetProcessed() ; + while ( m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) { + if ( m_mTree[nCToSplit].m_nParent != -2) + nCToSplit = m_mTree[nCToSplit].m_nParent ; + if ( m_mTree[m_mTree[nCToSplit].m_nChild1].IsProcessed() && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) + m_mTree[nCToSplit].SetProcessed() ; + if ( nCToSplit == -1 && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed()) + break ; + } + nCToSplit = m_mTree[nCToSplit].m_nChild2 ; + } + } + else { + nCToSplit = m_mTree[nCToSplit].m_nChild1 ; + } + } + } + return true ; +} + +//---------------------------------------------------------------------------- +void +Tree::Balance() +{ + //for ( int i : vCheck) { + // // non ancora implementato + // // rendo il tree balanced : ogni foglia deve avere una profondità di +- 1 rispetto alle foglie adiacenti. + //} + + // al momento il problema viene bypassato in fase di generazione dei poligoni, considerando per ogni cella, oltre ai propri vertici + // i vertici dei vicini che giacciono sui suoi lati +} + +//---------------------------------------------------------------------------- +void +Tree::GetTopNeigh( const int& nId, INTVECTOR& vTopNeighs) const +{ + if ( (int) vTopNeighs.size() == 0) { + if ( m_mTree.at( nId).m_nTop == -2) + return ; + if ( m_mTree.at( m_mTree.at( nId).m_nTop).IsLeaf()) + vTopNeighs.push_back( m_mTree.at( nId).m_nTop) ; + else { + if ( m_mTree.at( m_mTree.at( nId).m_nTop).IsSplitVert()) { + // se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( m_mTree.at( nId).m_nTop).GetTopRight().x - m_mTree.at( m_mTree.at( nId).m_nTop).GetBottomLeft().x <= + m_mTree.at( nId).GetTopRight().x - m_mTree.at( nId).GetBottomLeft().x) { + vTopNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild1) ; + vTopNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else{ + if ( m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild1).GetTopRight().x <= m_mTree.at( nId).GetBottomLeft().x || + m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild1).GetBottomLeft().x >= m_mTree.at( nId).GetTopRight().x ) + vTopNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild2) ; + else + vTopNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild1) ; + } + } + else { + vTopNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nTop).m_nChild2) ; + } + } + bool bAllLeaves = true ; + for ( int i : vTopNeighs) { + if ( ! m_mTree.at( i).IsLeaf()) + bAllLeaves = false ; + } + if ( ! bAllLeaves) + // almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child + GetTopNeigh( nId, vTopNeighs) ; + } + else { + for ( int j = 0 ; j != (int) vTopNeighs.size() ; ++ j) { + int i = vTopNeighs.at( j) ; + if ( m_mTree.at( i).IsLeaf()) + continue ; + else { + // se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child + vTopNeighs.erase( remove( vTopNeighs.begin(),vTopNeighs.end(),i)) ; + -- j ; + if ( m_mTree.at( i).IsSplitVert()) { + // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( i).GetTopRight().x - m_mTree.at( i).GetBottomLeft().x <= + m_mTree.at( nId).GetTopRight().x - m_mTree.at( nId).GetBottomLeft().x) { + vTopNeighs.push_back( m_mTree.at( i).m_nChild1) ; + vTopNeighs.push_back( m_mTree.at( i).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else { + if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().x <= m_mTree.at( nId).GetBottomLeft().x || + m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().x >= m_mTree.at( nId).GetTopRight().x ) + vTopNeighs.push_back( m_mTree.at( i).m_nChild2) ; + else + vTopNeighs.push_back( m_mTree.at( i).m_nChild1) ; + } + } + else { + vTopNeighs.push_back( m_mTree.at( i).m_nChild2) ; + } + } + } + } + std::vector vCells ; + for ( int k : vTopNeighs) + vCells.push_back( m_mTree.at( k)) ; + std::sort( vCells.begin(), vCells.end(), Cell::minorX) ; + vTopNeighs.clear() ; + for ( Cell c : vCells) + vTopNeighs.push_back( c.m_nId) ; +} + + +//---------------------------------------------------------------------------- +void +Tree::GetBottomNeigh( const int& nId, INTVECTOR& vBottomNeighs) const +{ + if ( (int) vBottomNeighs.size() == 0) { + if ( m_mTree.at( nId).m_nBottom == -2) + return ; + if ( m_mTree.at( m_mTree.at( nId).m_nBottom).IsLeaf()) + vBottomNeighs.push_back( m_mTree.at( nId).m_nBottom) ; + else { + if ( m_mTree.at( m_mTree.at( nId).m_nBottom).IsSplitVert()) { + // se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( m_mTree.at( nId).m_nBottom).GetTopRight().x - m_mTree.at( m_mTree.at( nId).m_nBottom).GetBottomLeft().x <= + m_mTree.at( nId).GetTopRight().x - m_mTree.at( nId).GetBottomLeft().x) { + vBottomNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild1) ; + vBottomNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else{ + if ( m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild1).GetTopRight().x <= m_mTree.at( nId).GetBottomLeft().x || + m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild1).GetBottomLeft().x >= m_mTree.at( nId).GetTopRight().x ) + vBottomNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild2) ; + else + vBottomNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild1) ; + } + } + else { + vBottomNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nBottom).m_nChild1) ; + } + } + bool bAllLeaves = true ; + for ( int i : vBottomNeighs) { + if ( ! m_mTree.at( i).IsLeaf()) + bAllLeaves = false ; + } + if ( ! bAllLeaves) + // almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child + GetBottomNeigh( nId, vBottomNeighs) ; + } + else { + for ( int j = 0 ; j != (int) vBottomNeighs.size() ; ++ j) { + int i = vBottomNeighs.at( j) ; + if ( m_mTree.at( i).IsLeaf()) + continue ; + else { + // se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child + vBottomNeighs.erase( remove( vBottomNeighs.begin(),vBottomNeighs.end(),i)) ; + -- j ; + if ( m_mTree.at( i).IsSplitVert()) { + // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( i).GetTopRight().x - m_mTree.at( i).GetBottomLeft().x <= + m_mTree.at( nId).GetTopRight().x - m_mTree.at( nId).GetBottomLeft().x) { + vBottomNeighs.push_back( m_mTree.at( i).m_nChild1) ; + vBottomNeighs.push_back( m_mTree.at( i).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else { + if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().x <= m_mTree.at( nId).GetBottomLeft().x || + m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().x >= m_mTree.at( nId).GetTopRight().x) + vBottomNeighs.push_back( m_mTree.at( i).m_nChild2) ; + else + vBottomNeighs.push_back( m_mTree.at( i).m_nChild1) ; + } + } + else { + vBottomNeighs.push_back( m_mTree.at( i).m_nChild1) ; + } + } + } + } + std::vector vCells ; + for ( int k : vBottomNeighs) + vCells.push_back( m_mTree.at( k)) ; + std::sort( vCells.begin(), vCells.end(), Cell::minorX) ; + vBottomNeighs.clear() ; + for ( Cell c : vCells) + vBottomNeighs.push_back( c.m_nId) ; +} + + +//---------------------------------------------------------------------------- +void +Tree::GetLeftNeigh( const int& nId, INTVECTOR& vLeftNeighs) const +{ + if ( (int) vLeftNeighs.size() == 0) { + if ( m_mTree.at( nId).m_nLeft == -2) + return ; + if ( m_mTree.at( m_mTree.at( nId).m_nLeft).IsLeaf()) + vLeftNeighs.push_back( m_mTree.at( nId).m_nLeft) ; + else { + if ( ! m_mTree.at( m_mTree.at( nId).m_nLeft).IsSplitVert()) { + // se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( m_mTree.at( nId).m_nLeft).GetTopRight().y - m_mTree.at( m_mTree.at( nId).m_nLeft).GetBottomLeft().y <= + m_mTree.at( nId).GetTopRight().y - m_mTree.at( nId).GetBottomLeft().y) { + vLeftNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild1) ; + vLeftNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else{ + if ( m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild1).GetTopRight().y <= m_mTree.at( nId).GetBottomLeft().y || + m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild1).GetBottomLeft().y >= m_mTree.at( nId).GetTopRight().y) + vLeftNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild2) ; + else + vLeftNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild1) ; + } + } + else { + vLeftNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nLeft).m_nChild2) ; + } + } + bool bAllLeaves = true ; + for ( int i : vLeftNeighs) { + if ( ! m_mTree.at( i).IsLeaf()) + bAllLeaves = false ; + } + if ( ! bAllLeaves) + // almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child + GetLeftNeigh( nId, vLeftNeighs) ; + } + else { + for ( int j = 0 ; j != (int) vLeftNeighs.size() ; ++ j) { + int i = vLeftNeighs.at( j) ; + if ( m_mTree.at( i).IsLeaf()) + continue ; + else { + // se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child + vLeftNeighs.erase( remove( vLeftNeighs.begin(),vLeftNeighs.end(),i)) ; + -- j ; + if ( ! m_mTree.at( i).IsSplitVert()) { + // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( i).GetTopRight().y - m_mTree.at( i).GetBottomLeft().y <= + m_mTree.at( nId).GetTopRight().y - m_mTree.at( nId).GetBottomLeft().y) { + vLeftNeighs.push_back( m_mTree.at( i).m_nChild1) ; + vLeftNeighs.push_back( m_mTree.at( i).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else { + if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().y <= m_mTree.at( nId).GetBottomLeft().y || + m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().y >= m_mTree.at( nId).GetTopRight().y) + vLeftNeighs.push_back( m_mTree.at( i).m_nChild2) ; + else + vLeftNeighs.push_back( m_mTree.at( i).m_nChild1) ; + } + } + else { + vLeftNeighs.push_back( m_mTree.at( i).m_nChild2) ; + } + } + } + } + std::vector vCells ; + for ( int k : vLeftNeighs) + vCells.push_back( m_mTree.at( k)) ; + std::sort( vCells.begin(), vCells.end(), Cell::minorY) ; + vLeftNeighs.clear() ; + for ( Cell c : vCells) + vLeftNeighs.push_back( c.m_nId) ; +} + +//---------------------------------------------------------------------------- +void +Tree::GetRightNeigh( const int& nId, INTVECTOR& vRightNeighs) const +{ + if ( (int) vRightNeighs.size() == 0) { + if ( m_mTree.at( nId).m_nRight == -2) + return ; + if ( m_mTree.at( m_mTree.at( nId).m_nRight).IsLeaf()) + vRightNeighs.push_back( m_mTree.at( nId).m_nRight) ; + else { + if ( ! m_mTree.at( m_mTree.at( nId).m_nRight).IsSplitVert()) { + // se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( m_mTree.at( nId).m_nRight).GetTopRight().y - m_mTree.at( m_mTree.at( nId).m_nRight).GetBottomLeft().y <= + m_mTree.at( nId).GetTopRight().y - m_mTree.at( nId).GetBottomLeft().y) { + vRightNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild1) ; + vRightNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else{ + if ( m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild1).GetTopRight().y <= m_mTree.at( nId).GetBottomLeft().y || + m_mTree.at( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild1).GetBottomLeft().y >= m_mTree.at( nId).GetTopRight().y) + vRightNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild2) ; + else + vRightNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild1) ; + } + } + else { + vRightNeighs.push_back( m_mTree.at( m_mTree.at( nId).m_nRight).m_nChild1) ; + } + } + bool bAllLeaves = true ; + for ( int i : vRightNeighs) { + if ( ! m_mTree.at( i).IsLeaf()) + bAllLeaves = false ; + } + if ( ! bAllLeaves) + // almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child + GetRightNeigh( nId, vRightNeighs) ; + } + else { + for ( int j = 0 ; j != (int) vRightNeighs.size() ; ++ j) { + int i = vRightNeighs.at( j) ; + if ( m_mTree.at( i).IsLeaf()) + continue ; + else { + // se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child + vRightNeighs.erase( remove( vRightNeighs.begin(),vRightNeighs.end(), i)) ; + -- j ; + if ( ! m_mTree.at( i).IsSplitVert()) { + // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima + if ( m_mTree.at( i).GetTopRight().y - m_mTree.at( i).GetBottomLeft().y <= + m_mTree.at( nId).GetTopRight().y - m_mTree.at( nId).GetBottomLeft().y) { + vRightNeighs.push_back( m_mTree.at( i).m_nChild1) ; + vRightNeighs.push_back( m_mTree.at( i).m_nChild2) ; + } + // altrimenti solo uno dei figli lo sarà + else { + if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().y <= m_mTree.at( nId).GetBottomLeft().y || + m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().y >= m_mTree.at( nId).GetTopRight().y) + vRightNeighs.push_back( m_mTree.at( i).m_nChild2) ; + else + vRightNeighs.push_back( m_mTree.at( i).m_nChild1) ; + } + } + else { + vRightNeighs.push_back( m_mTree.at( i).m_nChild1) ; + } + } + } + } + std::vector vCells ; + for ( int k : vRightNeighs) + vCells.push_back( m_mTree.at( k)) ; + std::sort( vCells.begin(), vCells.end(), Cell::minorY) ; + vRightNeighs.clear() ; + for ( Cell c : vCells) + vRightNeighs.push_back( c.m_nId) ; +} + +//---------------------------------------------------------------------------- +void +Tree::GetRootNeigh( const int& nEdge, INTVECTOR& vNeigh) +{ + int nId = -1 ; + bool bMod = false ; + if ( nEdge == 0) { + if ( m_mTree[nId].m_nBottom == -2) { + m_mTree[nId].m_nBottom = -1 ; + bMod = true ; + } + GetBottomNeigh( nId, vNeigh) ; + if ( bMod) + m_mTree[nId].m_nBottom = -2 ; + } + else if ( nEdge == 1) { + if ( m_mTree[nId].m_nRight == -2) { + m_mTree[nId].m_nRight = -1 ; + bMod = true ; + } + GetRightNeigh( nId, vNeigh) ; + if ( bMod) + m_mTree[nId].m_nRight = -2 ; + } + else if ( nEdge == 2) { + if ( m_mTree[nId].m_nTop == -2) { + m_mTree[nId].m_nTop = -1 ; + bMod = true ; + } + GetTopNeigh( nId, vNeigh) ; + if ( bMod) + m_mTree[nId].m_nTop = -2 ; + } + else if ( nEdge == 3) { + if ( m_mTree[nId].m_nLeft == -2) { + m_mTree[nId].m_nLeft = -1 ; + bMod = true ; + } + GetLeftNeigh( nId, vNeigh) ; + if ( bMod) + m_mTree[nId].m_nLeft = -2 ; + } +} + +//---------------------------------------------------------------------------- +int +Tree::GetHeightLeaves( const int& nId, INTVECTOR& vnLeaves, int d) const +{ + if ( nId == -1 && m_mTree.at( -1).IsLeaf()) { + vnLeaves.push_back( -1) ; + return 0 ; + } + else { + if ( (int) vnLeaves.size() == 0) { + if ( m_mTree.at( nId).IsLeaf()) + return d ; + else { + vnLeaves.push_back( m_mTree.at( nId).m_nChild1) ; + vnLeaves.push_back( m_mTree.at( nId).m_nChild2) ; + if ( ! m_mTree.at( m_mTree.at( nId).m_nChild1).IsLeaf() || ! m_mTree.at( m_mTree.at( nId).m_nChild2).IsLeaf()) + // almeno un child non è leaf quindi devo richiamare ricorsivamente questa funzione sui child in questione + d = GetHeightLeaves( nId, vnLeaves, m_mTree.at( m_mTree.at( nId).m_nChild1).m_nDepth) ; + } + } + else { + for ( int j = 0 ; j != (int) vnLeaves.size() ; ++ j) { + int i = vnLeaves.at( j) ; + if ( m_mTree.at( i).IsLeaf()) { + continue ; + } + else { + // se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child + vnLeaves.erase( remove( vnLeaves.begin(),vnLeaves.end(),i)) ; + -- j ; + vnLeaves.push_back( m_mTree.at( i).m_nChild1) ; + vnLeaves.push_back( m_mTree.at( i).m_nChild2) ; + d = std::max ( d, m_mTree.at( m_mTree.at( i).m_nChild1).m_nDepth) ; + } + } + return d ; + } + return d - m_mTree.at( nId).m_nDepth ; + } +} + +//---------------------------------------------------------------------------- +int +Tree::GetDepth( const int& nId, const int& nRef = -2) const +{ + int c = 0 ; + int nCell = nId ; + while ( m_mTree.at( nCell).m_nParent != nRef) { + nCell = m_mTree.at( nCell).m_nParent ; + ++ c ; + } + return c ; +} + +struct generator +{ + int value ; + generator() { value = -1 ;} + int operator() () { return ++value ; } +} ; + +//---------------------------------------------------------------------------- +bool +Tree::GetPolygons( std::vector& vPolygons) { + if ( (int) m_vPolygons.size() == 0) { + if ( ! m_bTrimmed) { + vPolygons.clear() ; + POLYLINEVECTOR vPolygonsBasic ; + GetPolygonsBasic( vPolygonsBasic) ; + for ( PolyLine pl : vPolygonsBasic) { + POLYLINEVECTOR vSinglePolygon ; + vSinglePolygon.push_back( pl) ; + vPolygons.push_back( vSinglePolygon) ; + } + return true ; + } + // trimmata + else { + POLYLINEVECTOR vPolygonsBasic ; + GetPolygonsBasic( vPolygonsBasic) ; + if ( ! TraceLoopLabelCell( vPolygonsBasic)) + return false ; + // scorro sulle celle e costruisco i poligoni + int nCells = (int)vPolygonsBasic.size() ; + for ( int i = 0 ; i < nCells ; ++i) { + + // costruisco i poligoni partendo dal vettore delle intersezioni, come spiegato a pag15 di Cripps + int nId = m_vnLeaves[i] ; + if ( m_mTree[nId].m_nFlag == 4) { + // vettore dei poligoni ( loop) della cella nId + POLYLINEVECTOR vCellPolygons ; + vCellPolygons.push_back( vPolygonsBasic[i]) ; + vPolygons.push_back( vCellPolygons) ; + } + else if ( m_mTree[nId].m_nFlag == 0) + continue ; + else { + // vettore in cui salvo il chunk di appartenenza di ogni loop che attraversa la cella + INTVECTOR vnParentChunk ; + // vettore in cui salvo i loop che non appartengono al poligono che sto cotruendo nel ciclo attuale e da cui ripasserò dopo + INTVECTOR vToCheck( (int) m_mTree[nId].m_vInters.size()) ; + std::generate_n( vToCheck.begin(), (int) m_mTree[nId].m_vInters.size(), generator()) ; + // numero di poligoni aggiunti + int nPoly = 0 ; + // scorro sui vettori intersezione della cella nId e sui suoi vertici + // in questo for analizzo solo i loop che tagliano la cella + while( (int)vToCheck.size() != 0) { + int nPolyBefore = nPoly ; + CreateCellPolygons( i, vPolygons, vToCheck, nPoly, vnParentChunk, vPolygonsBasic[i]) ; + if ( nPolyBefore == nPoly) + break ; + } + // ora analizzo anche i loop che sono contenuti nella cella + CreateIslandAndHoles( i, vPolygons, nPoly, vnParentChunk) ; + } + } + return true ; + } + } + else { + vPolygons = m_vPolygons ; + return true ; + } +} + +//---------------------------------------------------------------------------- +bool +Tree::GetPolygonsBasic( POLYLINEVECTOR& vPolygons) +{ + if ( m_vPolygons.empty()) { + PNTVECTOR vVertices ; + INTVECTOR vNeigh ; + // setto le celle che sono sul LeftEdge e sul TopEdge + if ( m_bClosedU) { + GetRootNeigh( 1, vNeigh) ; + for ( int k : vNeigh) { + m_mTree[k].m_bOnLeftEdge = true ; + } + vNeigh.clear() ; + } + if ( m_bClosedV) { + GetRootNeigh( 0, vNeigh) ; + for ( int k : vNeigh) { + m_mTree[k].m_bOnTopEdge = true ; + } + } + bool bBottomRight , bTopLeft ; + // scorro lungo tutte le celle leaves e oltre agli angoli della cella aggiungo alla polyline della cella anche i vertici, delle celle adiacenti, + // che sono sui lati della cella corrente + for ( int nId : m_vnLeaves) { + vVertices.clear() ; + vNeigh.clear() ; + vVertices.push_back( m_mTree.at( nId).GetBottomLeft()) ; + GetBottomNeigh( nId, vNeigh) ; + // aggiungo i vertici che sono sul lato bottom, solo se ho più di un vicino bottom + if ( (int) vNeigh.size() != 0 && (int) vNeigh.size() != 1){ + // se la superficie è chiusa lungo il parametro V e le celle vicine bottom sono sul lato Top + // devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico. + if ( m_bClosedV && m_mTree.at(vNeigh[0]).m_bOnTopEdge) { + for ( int j : vNeigh) { + Point3d pt( m_mTree.at( j).GetTopRight().x, m_mTree.at( nId).GetBottomLeft().y) ; + vVertices.push_back( pt) ; + } + } + else { + for ( int j : vNeigh) + vVertices.push_back( m_mTree.at( j).GetTopRight()) ; + } + bBottomRight = true ; + } + else + bBottomRight = false ; + vNeigh.clear() ; + GetRightNeigh ( nId, vNeigh) ; + // aggiungo i vertici che sono sul lato right, solo se ho più di un vicino right + if ( (int) vNeigh.size() != 0 && (int) vNeigh.size() != 1){ + // se la superficie è chiusa lungo il parametro U e le celle vicine right sono sul lato Left + // devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico. + if ( m_bClosedU && m_mTree.at( vNeigh[0]).m_bOnLeftEdge ) { + for ( int j : vNeigh) { + Point3d pt( m_mTree.at( nId).GetTopRight().x, m_mTree.at(j).GetBottomLeft().y) ; + vVertices.push_back( pt) ; + } + } + else { + for ( int j : vNeigh) + vVertices.push_back( m_mTree.at( j).GetBottomLeft()) ; + } + + } + // se non l'ho già aggiunto tramite i vicini bottom aggiungo il punto bottom right + else if ( ! bBottomRight) { + Point3d ptBr( m_mTree.at( nId).GetTopRight().x, m_mTree.at( nId).GetBottomLeft().y) ; + vVertices.push_back( ptBr) ; + } + vNeigh.clear() ; + vVertices.push_back( m_mTree.at( nId).GetTopRight()) ; + GetTopNeigh ( nId, vNeigh) ; + std::reverse( vNeigh.begin(), vNeigh.end()) ; + // aggiungo i vertici che sono sul lato top, solo se ho più di un vicino top + if ( (int) vNeigh.size() != 0 && (int) vNeigh.size() != 1) { + // se la superficie è chiusa lungo il parametro U e la cella è sul lato top + // devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico. + if ( m_bClosedU && m_mTree.at( nId).m_bOnTopEdge) { + for ( int j : vNeigh) { + Point3d pt( m_mTree.at( j).GetBottomLeft().x, m_mTree.at( nId).GetTopRight().y) ; + vVertices.push_back( pt) ; + } + } + else { + for ( int j : vNeigh) + vVertices.push_back( m_mTree.at( j).GetBottomLeft()) ; + } + bTopLeft = true ; + } + else + bTopLeft = false ; + vNeigh.clear() ; + GetLeftNeigh ( nId, vNeigh) ; + std::reverse( vNeigh.begin(), vNeigh.end()) ; + // aggiungo i vertici che sono sul lato left, solo se ho più di un vicino left + if ( (int) vNeigh.size() != 0 && (int) vNeigh.size() != 1) { + // se la superficie è chiusa lungo il parametro U e la cella è sul lato left + // devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico. + if ( m_bClosedU && m_mTree.at( nId).m_bOnLeftEdge) { + for ( int j : vNeigh) { + Point3d pt( m_mTree.at( nId).GetBottomLeft().x, m_mTree.at(j).GetTopRight().y) ; + vVertices.push_back( pt) ; + } + } + else { + for ( int j : vNeigh) + vVertices.push_back( m_mTree.at( j).GetTopRight()) ; + } + } + // se non l'ho già aggiunto tramite i vicini top aggiungo il punto top left + else if ( ! bTopLeft) { + Point3d ptTl( m_mTree.at( nId).GetBottomLeft().x, m_mTree.at( nId).GetTopRight().y) ; + vVertices.push_back( ptTl) ; + } + vNeigh.clear() ; + vVertices.push_back( m_mTree.at( nId).GetBottomLeft()) ; + if ( ! m_bTrimmed) { + // se ho una cella con vicino dello stesso grado ( quindi il poligono ha solo 5 punti) controllo la curvatura nella cella e + // se necessario cambio l'ordine dei vertici per scegliere la diagonale di split migliore + if ( vVertices.size() == 5) { + Point3d ptPSrf, ptP00, ptP10, ptP11, ptP01 ; + double dU, dV ; + dU = ( m_mTree.at( nId).GetBottomLeft().x + m_mTree.at( nId).GetTopRight().x) / 2 / SBZ_TREG_COEFF ; + dV = ( m_mTree.at( nId).GetBottomLeft().y + m_mTree.at( nId).GetTopRight().y) / 2 / SBZ_TREG_COEFF ; + m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; + ptP00 = m_mVert.at( nId).at( 0) ; + ptP10 = m_mVert.at( nId).at( 1) ; + ptP11 = m_mVert.at( nId).at( 2) ; + ptP01 = m_mVert.at( nId).at( 3) ; + Point3d ptP00P11 = ( ptP00 + ptP11) / 2 ; + Point3d ptP10P01 = ( ptP10 + ptP01) / 2 ; + // ho la curvatura maggiore sulla diagonale tra P10 e P01, ruoto l'ordine dei vertici, in modo che triangulate prenda la diagonale giusta + if ( Dist( ptP00P11, ptPSrf) + EPS_SMALL > Dist( ptP10P01, ptPSrf)) { + rotate( vVertices.begin(), vVertices.begin() + 1,vVertices.end()) ; + vVertices.back() = vVertices.at( 0) ; + } + } + } + + m_vPolygons.emplace_back() ; + m_vPolygons.back().emplace_back() ; + for ( int i = 0 ; i < (int) vVertices.size() ; ++i) { + m_vPolygons.back().back().AddUPoint( i, vVertices.at( i)) ; + } + + } + } + + // restituisco i poligoni delle celle del tree nello spazio parametrico + for ( int t = 0 ; t < (int)m_vPolygons.size() ; ++ t) { + for ( int z = 0 ; z < (int)m_vPolygons[t].size() ; ++z) { + vPolygons.push_back( m_vPolygons[t][z]) ; + } + } + return true ; +} + +//---------------------------------------------------------------------------- +void +Tree::ResetTree( void) +{ + // setto tutte le foglie a Processed = false + for ( int nC : m_vnLeaves) { + m_mTree[nC].SetProcessed( false) ; + } +} + +//---------------------------------------------------------------------------- +INTVECTOR +Tree::FindCell( const Point3d& ptToAssign, const CurveLine& clTrim) const +{ + INTVECTOR nCells ; + int nId = -1 ; + // se fallisce ritorna un vettore vuoto + // verifico che il punto sia all'interno dello spazio parametrico + if ( ptToAssign.x < m_mTree.at( -1).GetBottomLeft().x - EPS_SMALL || ptToAssign.x > m_mTree.at( -1).GetTopRight().x + EPS_SMALL|| + ptToAssign.y < m_mTree.at( -1).GetBottomLeft().y - EPS_SMALL || ptToAssign.y > m_mTree.at( -1).GetTopRight().y + EPS_SMALL) { + //nCells.push_back( - 2) ; + return nCells ; + } + + // se ho diviso preliminarmente le patches e in uno dei due parametri ho un numero dispari di patches devo individuare a mano la cella parent + // in cui individuare la foglia giusta + if ( m_bSplitPatches && ( m_nSpanU > 1 || m_nSpanV > 1)) { + INTVECTOR nParents = FindCell( ptToAssign, clTrim, m_vnParents) ; + nId = nParents.back() ; + } + // individuo la foglia in cui ho lo start del loop + while ( ! m_mTree.at( nId).IsLeaf()) { + if ( m_mTree.at( nId).IsSplitVert()) { + double dMid = ( m_mTree.at( nId).GetBottomLeft().x + m_mTree.at( nId).GetTopRight().x) / 2 ; + if ( ptToAssign.x < dMid + EPS_SMALL) { + nId = m_mTree.at( nId).m_nChild1 ; + } + else { + nId = m_mTree.at( nId).m_nChild2 ; + } + } + else { + double dMid = ( m_mTree.at( nId).GetBottomLeft().y + m_mTree.at( nId).GetTopRight().y) / 2 ; + if ( ptToAssign.y < dMid + EPS_SMALL) { + nId = m_mTree.at( nId).m_nChild2 ; + } + else { + nId = m_mTree.at( nId).m_nChild1 ; + } + } + } + + nCells.push_back( nId) ; + + // se sono in un vertice o su un lato devo controllare di aver trovato la cella giusta + Point3d ptBr( m_mTree.at( nId).GetTopRight().x , m_mTree.at( nId).GetBottomLeft().y) ; + Point3d ptTl( m_mTree.at( nId).GetBottomLeft().x , m_mTree.at( nId).GetTopRight().y) ; + if ( abs( ptToAssign.x - ptTl.x) < EPS_SMALL || abs( ptToAssign.x - ptBr.x) < EPS_SMALL || + abs( ptToAssign.y - ptTl.y) < EPS_SMALL || abs( ptToAssign.y - ptBr.y) < EPS_SMALL) + { + Point3d ptToAssignPlus ; + double dParam ; + Vector3d vDir ; + clTrim.GetParamAtPoint( ptToAssign, dParam, EPS_SMALL) ; + clTrim.GetPointTang( dParam + EPS_SMALL, ICurve::FROM_MINUS, ptToAssignPlus, vDir) ; + if ( abs( vDir.x) > 1 - EPS_SMALL || abs( vDir.y) > 1 - EPS_SMALL) { + vDir.Rotate( Z_AX, -90) ; + ptToAssignPlus = ptToAssignPlus + vDir * 2 * EPS_SMALL ; + } + nCells = FindCell( ptToAssignPlus, clTrim) ; + if ( nCells.empty()) { + ptToAssignPlus = ptToAssignPlus - 4 * vDir * EPS_SMALL ; + nCells = FindCell( ptToAssignPlus, clTrim) ; + } + } + return nCells ; +} + +//---------------------------------------------------------------------------- +INTVECTOR +Tree::FindCell( const Point3d& ptToAssign, const CurveLine& cl, INTVECTOR vCells) const +{ + // se non trova nulla restituisce un vettore vuoto + // restituisce sempre solo una cella + + // la CurveLine è il segmento di trim su cui giace ptToAssign + Point3d ptIntersPlus ; + double dParam ; + Vector3d vDir ; + cl.GetParamAtPoint( ptToAssign, dParam, EPS_SMALL) ; + // mi sposto appena più avanti di ptToAssign + cl.GetPointTang( dParam + EPS_SMALL, ICurve::FROM_MINUS, ptIntersPlus, vDir) ; + INTVECTOR nCells ; + int nId = -1 ; + for ( int nCell : vCells) { + if ( ptIntersPlus.x > m_mTree.at( nCell).GetBottomLeft().x - EPS_ZERO && ptIntersPlus.x < m_mTree.at( nCell).GetTopRight().x + EPS_ZERO && + ptIntersPlus.y > m_mTree.at( nCell).GetBottomLeft().y - EPS_ZERO && ptIntersPlus.y < m_mTree.at( nCell).GetTopRight().y + EPS_ZERO) { + nId = nCell ; + nCells.push_back( nId) ; + } + } + return nCells ; +} + +//---------------------------------------------------------------------------- +bool +Tree::TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) +{ + // l'idea è di seguire il loop di trim, passando di cella in cella, restando, quando possibile, a destra del loop. + // ( destra e sinistra sono determinate dal verso del loop). + + // il valore è negativo perché voglio considerare contenuto anche un punto che sta su un lato + double dLinTol = - EPS_SMALL ; + // percorro i loop trovando le interezioni con le celle e riempiendo i vettori m_vInters delle varie celle + for ( int i = 0 ; i < (int) m_vPlApprox.size() ; ++ i) { + PolyLine plLoop = std::get<0>(m_vPlApprox[i]) ; + // controllo se il loop è CCW o CW + bool bCCW = std::get<1>(m_vPlApprox[i]) ; + // trovo in quale cella è il ptStart + Point3d ptStart ; + plLoop.GetFirstPoint( ptStart) ; + PNTULIST lPt = plLoop.GetUPointList() ; + PNTULIST:: iterator ptFirst = lPt.begin() ; + PNTULIST:: iterator ptSecond = ptFirst ; + std::advance( ptSecond, 1) ; + CurveLine clFirst ; + clFirst.Set( ptFirst->first, ptSecond->first) ; + // individuo la cella da cui parte il loop + INTVECTOR nCells = FindCell( ptStart, clFirst) ; + int nId ; + if ( ! nCells.empty()) + nId = nCells.back() ; + else + // il loop è fuori dalla cella root che sto analizzando + continue ; + int nFirstCell = nId ; + // trovo quali punti della polyline sono nella cella e l'intersezione + PNTVECTOR vptInters ; + vptInters.push_back( ptStart) ; + // qui mi devo salvare quanti elementi ho già nel vettore m_vInters della cella + // per poter fare il merge con l'ultimo vptInters della curva di trim, che sarà ancora in questa cella + // e terminerà nel punto di start + int nPass = (int) m_mTree[nId].m_vInters.size() ; + m_mTree[nId].m_vInters.emplace_back() ; + // salvo il verso del loop + m_mTree[nId].m_vInters.back().bCCW = bCCW ; + // salvo il chunk del loop + m_mTree[nId].m_vInters.back().nChunk = m_mChunk[i] ; + bool bLoopInside = true ; + Point3d ptCurr ; + INTVECTOR :: iterator iter = find( m_vnLeaves.begin(), m_vnLeaves.end(), nId) ; + int nIdPolygon = std::distance( m_vnLeaves.begin(), iter) ; + bool bEraseNextPoint = false ; + while ( plLoop.GetNextPoint( ptCurr)) { + Point3d ptTStart, ptTEnd ; + plLoop.GetPrevPoint( ptTStart) ; + plLoop.GetNextPoint( ptTEnd) ; + CurveLine clTrim ; + clTrim.Set( ptTStart, ptTEnd) ; + // qui devo mettere una tolleranza negativa per poter tener conto anche dei punti che sono SULLA curva + while( ! IsPointInsidePolyLine( ptCurr, vplPolygons[nIdPolygon], dLinTol)) { + // sto uscendo dalla cella, quindi cerco l'intersezione + if ( bEraseNextPoint) { + vptInters.pop_back() ; + bEraseNextPoint = false ; + } + bLoopInside = false ; + // trovo l'intersezione e passo alla cella successiva. nId viene aggiornato dalla funzione FindInters + // se non trovo l'intersezione vuol dire che non sono nella cella giusta! + // al precedente FindInters avrei dovuto passare di cella + if ( ! FindInters( nId, clTrim, vptInters, true)) { + // scarterò il punto molto vicino al lato e tengo solo l'intersezione del trim col lato + vptInters.pop_back() ; + plLoop.GetPrevPoint( ptTEnd) ; + plLoop.GetPrevPoint( ptTStart) ; + plLoop.GetNextPoint( ptCurr) ; + clTrim.Set( ptTStart, ptTEnd) ; + clTrim.ExtendEndByLen( EPS_SMALL * 2) ; + clTrim.ExtendStartByLen( EPS_SMALL * 2) ; + if ( ! FindInters( nId, clTrim, vptInters, true)) + return false ; + bEraseNextPoint = true ; + } + // ricalcolo la posizione di nId nel vettore delle foglie + iter = find( m_vnLeaves.begin(), m_vnLeaves.end(), nId) ; + nIdPolygon = std::distance( m_vnLeaves.begin(), iter) ; + // salvo il verso del loop + m_mTree[nId].m_vInters.back().bCCW = bCCW ; + // salvo il chunk del loop + m_mTree[nId].m_vInters.back().nChunk = m_mChunk[i] ; + } + // aggiungo la fine del segmento nel vettore delle intersezioni + vptInters.push_back( ptCurr) ; + } + if ( nId == nFirstCell) + vptInters.pop_back() ; + m_mTree[nId].m_vInters.back().vpt = vptInters ; + if ( bLoopInside) { + // setto la categoria della cella + if ( m_mTree[nId].m_nFlag == -1) + m_mTree[nId].m_nFlag = 2 ; + else if ( m_mTree[nId].m_nFlag == 1) + m_mTree[nId].m_nFlag = 3 ; + // setto i lati di ingresso e uscita a -1 per indicare che ho un loop interno alla cella + m_mTree[nId].m_vInters.back().nIn = -1 ; + m_mTree[nId].m_vInters.back().nOut = -1 ; + } + // sono tornato alla cella di partenza, quindi devo fare il merge dei due vettori di intersezione che ho creato per questa cella + // per lo stesso loop + else { + // verifico se sono effettivamente nella cella di partenza o in una cella adiacente + if ( nId != nFirstCell) { + Point3d ptFirst = m_mTree[nFirstCell].m_vInters[nPass].vpt[0] ; + Point3d ptLast = m_mTree[nId].m_vInters.back().vpt.back() ; + //sistemo l'ingresso della prima cella + int nEdge ; + OnWhichEdge( nFirstCell, ptFirst, nEdge) ; + m_mTree[nFirstCell].m_vInters[nPass].nIn = nEdge ; + // sistemo l'uscita dell'ultima cella + OnWhichEdge( nId, ptLast, nEdge) ; + m_mTree[nId].m_vInters.back().nOut = nEdge ; + // sistemo il flag dell'ultima cella + if ( m_mTree[nId].m_nFlag == -1) + m_mTree[nId].m_nFlag = 1 ; + else if ( m_mTree[nId].m_nFlag == 2) + m_mTree[nId].m_nFlag = 3 ; + } + // sono tornato nella cella iniziale, quindi giunto i due vettori intersezione + else if ( nId == nFirstCell) { + int nOut = m_mTree[nId].m_vInters[nPass].nOut ; + m_mTree[nId].m_vInters.back().vpt.insert( m_mTree[nId].m_vInters.back().vpt.end(), + m_mTree[nId].m_vInters[nPass].vpt.begin(), + m_mTree[nId].m_vInters[nPass].vpt.end()) ; + m_mTree[nId].m_vInters[nPass] = m_mTree[nId].m_vInters.back() ; + m_mTree[nId].m_vInters.pop_back() ; + // sistemo il lato d'uscita + m_mTree[nId].m_vInters[nPass].nOut = nOut ; + } + } + } + + // riordino i vettori di intersezione per ogni cella e setto il flag RightEdgeIn + for ( int nId : m_vnLeaves) { + std::sort( m_mTree[nId].m_vInters.begin(), m_mTree[nId].m_vInters.end()) ; + SetRightEdgeIn( nId) ; + } + + // devo riconoscere le celle dentro i loop che sono ancora con label nFlag2 = 0 + ResetTree() ; + INTVECTOR vNeigh, vFirst ; + GetRootNeigh( 1, vFirst) ; + for ( int k : vFirst) { + m_mTree[k].m_bOnLeftEdge = true ; + } + int nLastLeft = vFirst.back() ; + int nCell = vFirst[0] ; + GetRightNeigh( nCell, vNeigh) ; + // proseguo finché non sono sull'elemento più alto di vFirst e tutti i suoi vicini sono processati/categorizzati + bool bAllDone = false ; + // mentre scorro a destra mi basta trovare un vicino da cui non sono passato + // mentre torno indietro a sinistra, mi fermo quando trovo una cella non processata ( con vicini a destra da cui non sono passato) + while ( ! bAllDone) { + // categorizzo la cella + m_mTree[nCell].m_nFlag2 = 1 ; + CategorizeCell( nCell) ; + bool bDone = false ; + // fintanto che la cella ha tra i vicini a destra una cella non elaborata mi sposto a destra + // definisco una cella Processed se tutto il ramo a destra è categorizzato + while ( ! m_mTree[nCell].IsProcessed()) { + // verso la cella a destra più in basso da cui non sono ancora passato + bool bProceeded = false ; + for ( int i = 0 ; i < (int)vNeigh.size() ; ++ i) { + if ( m_mTree[vNeigh[i]].m_nFlag2 == 0) { + nCell = vNeigh[i] ; + bProceeded = true ; + break ; + } + } + if ( ! bProceeded) { + m_mTree[nCell].SetProcessed() ; + } + else { + //categorizzo la cella + m_mTree[nCell].m_nFlag2 = 1 ; + CategorizeCell( nCell) ; + } + + if ( ! m_mTree[nCell].IsProcessed()) { + // guardo i vicini a destra per passare alla prossima cella + vNeigh.clear() ; + GetRightNeigh( nCell, vNeigh) ; + bDone = true ; + //controllo che tra i vicini di destra ce ne sia almeno uno da cui non sono passato + // ( e controllo che non sia una cella sul lato sinistro ( adiacenza in caso di superficie chiusa)) + for ( int t: vNeigh) { + if ( m_mTree[t].m_nFlag2 == 0 ) { + if ( ! m_bClosedU) { + bDone = false ; + break ; + } + else { + // controllo che non sia sul lato sinistro + if ( ! m_mTree[t].m_bOnLeftEdge) { + bDone = false ; + break ; + } + } + } + } + m_mTree[nCell].SetProcessed( bDone) ; + } + } + vNeigh.clear() ; + GetRightNeigh( nCell, vNeigh) ; + // se non ho vicini a destra o se i vicini sono già tutti categorizzati + // torno indietro a sinistra alla cella già categorizzata più bassa + while ( m_mTree[nCell].IsProcessed()) { + // trovo il vicino a sinistra, già categorizzato, più basso + vNeigh.clear() ; + GetLeftNeigh( nCell, vNeigh) ; + // però se sono già sul lato sinistro non cerco di spostarmi ancora più a sinistra. + if ( ! m_mTree[nCell].m_bOnLeftEdge) { + for ( int p = 0 ; p < (int)vNeigh.size() ; ++ p) { + if ( m_mTree[vNeigh[p]].m_nFlag2 != 0) { + nCell = vNeigh[p] ; + break ; + } + } + } + if ( ! m_bClosedU) { + // se non ho vicini a sinistra sono tornato sul lato sinistro + // se questa cella non è processata devo procedere alla cella più bassa non processata sul lato sinistro + if ( vNeigh.empty() && m_mTree[nCell].IsProcessed()) { + for ( int p = 0 ; p < (int)vFirst.size() ; ++ p) { + if ( m_mTree[vFirst[p]].m_nFlag2 == 0) { + nCell = vFirst[p] ; + break ; + } + } + if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()) { + bAllDone = true ; + break ; + } + } + } + else { + // verifico se sono tornato sul lato sinistro, in questo caso procedo con la prima cella di vFirst non processata + if ( m_mTree[nCell].m_bOnLeftEdge && m_mTree[nCell].IsProcessed()) { + for ( int p = 0 ; p < (int) vFirst.size(); ++ p) { + if ( nCell == vFirst[p] && nCell != nLastLeft) { + nCell = vFirst[p + 1] ; + break ; + } + else if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()){ + bAllDone = true ; + } + } + } + if ( bAllDone) + break ; + } + // controllo se tutti i vicini di destra sono categorizzati + vNeigh.clear() ; + GetRightNeigh( nCell, vNeigh) ; + bool bDone = true ; + for ( int k = 0 ; k < (int)vNeigh.size() ; ++ k) { + if ( ! m_mTree[vNeigh[k]].IsProcessed()) { + bDone = false ; + break ; + } + } + if ( bDone) { + m_mTree[nCell].SetProcessed( bDone) ; + if ( m_mTree[nCell].m_nFlag2 == 0) { + m_mTree[nCell].m_nFlag2 = 1 ; + CategorizeCell( nCell) ; + } + } + } + vNeigh.clear() ; + GetRightNeigh( nCell, vNeigh) ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::FindInters( int& nId, const CurveLine& clTrim, PNTVECTOR& vptInters, bool bFirstInters) +{ + CurveLine clEdge , clEdge2 ; + Point3d ptStart , ptEnd ; + clTrim.GetStartPoint( ptStart) ; + clTrim.GetEndPoint( ptEnd) ; + // trovo da quale lato sto uscendo + Point3d ptTR = m_mTree[nId].GetTopRight() ; + Point3d ptBL = m_mTree[nId].GetBottomLeft() ; + Point3d ptTl( ptBL.x , ptTR.y) ; + Point3d ptBr( ptTR.x , ptBL.y) ; + int nEdge ; // flag che indica il lato su cui ho l'intersezione a partire dal lato top in senso antiorario + // oltre il 3 sono le celle adiacenti in diagonale al vertice-> 4 corrisponde al ptTl e da lì in senso antiorario + // -1 se la curva è sempre dentro la cella + bool bIntersFound = false ; + Point3d ptInters ; + int nEdge2 ; + if ( ptEnd.y >= ptTR.y && ptEnd.x <= ptTR.x) { + nEdge = 0 ; + // lato sopra + clEdge.Set( ptTR, ptTl) ; + // lato sinistro + if ( ptEnd.x < ptBL.x) { + nEdge2 = 1 ; + clEdge2.Set( ptTl, ptBL) ; + } + //else if ( AreSamePointExact( ptEnd, ptTl) && bFirstInters) { + // nEdge = 4 ; + // ptInters = ptTl ; + // bIntersFound = true ; + //} + //else if ( AreSamePointExact( ptEnd, ptTR) && bFirstInters) { + // nEdge = 7 ; + // ptInters = ptTR ; + // bIntersFound = true ; + //} + } + else if ( ptEnd.x <= ptBL.x && ptEnd.y <= ptTR.y) { + nEdge = 1 ; + // lato sinistro + clEdge.Set( ptTl, ptBL) ; + // lato sotto + if ( ptEnd.y < ptBL.y) { + nEdge2 = 2 ; + clEdge2.Set( ptBL, ptBr) ; + } + //else if ( AreSamePointExact( ptEnd, ptBL) && bFirstInters) { + // nEdge = 5 ; + // ptInters = ptBL ; + // bIntersFound = true ; + //} + //else if ( AreSamePointExact( ptEnd, ptTl) && bFirstInters) { + // nEdge = 4 ; + // ptInters = ptTl ; + // bIntersFound = true ; + //} + } + else if ( ptEnd.y <= ptBL.y && ptEnd.x >= ptBL.x) { + nEdge = 2 ; + // lato sotto + clEdge.Set( ptBL, ptBr) ; + // lato destro + if ( ptEnd.x > ptTR.x) { + nEdge2 = 3 ; + clEdge2.Set( ptBr, ptTR) ; + } + //else if ( AreSamePointExact( ptEnd, ptBr) && bFirstInters) { + // nEdge = 6 ; + // ptInters = ptBr ; + // bIntersFound = true ; + //} + //else if ( AreSamePointExact( ptEnd, ptBL) && bFirstInters) { + // nEdge = 5 ; + // ptInters = ptBL ; + // bIntersFound = true ; + //} + } + else if ( ptEnd.x >= ptTR.x && ptEnd.y >= ptBL.y) { + nEdge = 3 ; + // lato desto + clEdge.Set( ptBr, ptTR) ; + // lato sopra + if ( ptEnd.y > ptTR.y) { + nEdge2 = 0 ; + clEdge2.Set( ptTR, ptTl) ; + } + //else if ( AreSamePointExact( ptEnd, ptTR) && bFirstInters) { + // nEdge = 7 ; + // ptInters = ptTR ; + // bIntersFound = true ; + //} + //else if ( AreSamePointExact( ptEnd, ptBr) && bFirstInters) { + // nEdge = 6 ; + // ptInters = ptBr ; + // bIntersFound = true ; + //} + } + else + return false ; + + bool bIntersOn2Found = false ; + if ( ! bIntersFound) { + // intersezione e controlli + IntersLineLine illExit( clTrim, clEdge, true) ; + IntCrvCrvInfo aInfo, aInfo2 ; + bool bIntersOn1Found = true ; + if ( ! illExit.GetIntCrvCrvInfo( aInfo)) { + bIntersOn1Found = false ; + if ( ! clEdge2.IsValid()) + return false ; + } + else if ( aInfo.bOverlap && ! bFirstInters) { + ptInters = aInfo.IciA[1].ptI ; + bIntersFound = true ; + } + if ( clEdge2.IsValid() && ! bIntersFound){ + IntersLineLine illExit2( clTrim, clEdge2, true) ; + // verifico su quale dei due lati ho l'intersezione + if ( ! illExit2.GetIntCrvCrvInfo( aInfo2)){ + if ( bIntersOn1Found) { + // se ho intersezione su Edge1 con sovrapposizione, seleziono o il primo punto o il secondo. + if ( aInfo.bOverlap && ! bFirstInters) + ptInters = aInfo.IciA[1].ptI ; + else + ptInters = aInfo.IciA[0].ptI ; + } + else + return false ; + } + else { + //// solo intersezione sul lato 2 + bIntersOn2Found = true ; + if ( aInfo2.bOverlap && ! bFirstInters) + ptInters = aInfo2.IciA[1].ptI ; + else + ptInters = aInfo2.IciA[0].ptI ; + } + } + else + ptInters = aInfo.IciA[0].ptI ; // qui devo mettere il controllo su prima o seconda intersezione? + } + if ( ! bIntersOn2Found) + OnWhichEdge( nId, ptInters, nEdge) ; + else + nEdge = nEdge2 ; + m_mTree[nId].m_vInters.back().nOut = nEdge ; + if ( (int)vptInters.size() == 0 || ! AreSamePointExact( ptInters , vptInters.back())) + vptInters.push_back( ptInters) ; + // salvo il vettore intersezione per la cella e capisco in quale altra cella passare + if ( (int)vptInters.size() == 1) + m_mTree[nId].m_vInters.back().vpt.push_back( vptInters[0]) ; + else + m_mTree[nId].m_vInters.back().vpt = vptInters ; + vptInters.clear() ; + // setto la categoria della cella + if ( m_mTree[nId].m_nFlag == -1) + m_mTree[nId].m_nFlag = 1 ; + else if ( m_mTree[nId].m_nFlag == 2) + m_mTree[nId].m_nFlag = 3 ; + + // seleziono la cella successiva da analizzare + // + // se la superficie è chiusa su un parametro devo stare attento a considerare lo spazio parametrico come se non fosse periodico + // es: non posso saltare dal lato destro al lato sinistro anche se una cella sul lato sinistro risulta la RightNeigh di una cella sul lato destro! + INTVECTOR vNeigh, vNeigh1 ; + if ( nEdge == 0) { + GetTopNeigh( nId, vNeigh) ; + std::reverse( vNeigh.begin(), vNeigh.end()) ; + for ( int j : vNeigh) { + if ( ptInters.x >= m_mTree[j].GetBottomLeft().x) { + nId = j ; + break ; + } + } + m_mTree[nId].m_vInters.emplace_back() ; + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( ptInters.x > m_mTree[nId].GetBottomLeft().x + EPS_SMALL && ptInters.x < m_mTree[nId].GetTopRight().x - EPS_SMALL) + m_mTree[nId].m_vInters.back().nIn = 2 ; + else if ( AreSamePointApprox( ptInters, m_mTree[nId].GetBottomLeft())) + m_mTree[nId].m_vInters.back().nIn = 5 ; + else if ( AreSamePointApprox( ptInters, ptBr)) + m_mTree[nId].m_vInters.back().nIn = 6 ; + else + m_mTree[nId].m_vInters.back().nIn = 2 ; + } + else if ( nEdge == 1) { + GetLeftNeigh( nId, vNeigh) ; + std::reverse( vNeigh.begin(), vNeigh.end()) ; + for ( int j : vNeigh) { + if ( ptInters.y >= m_mTree[j].GetBottomLeft().y) { + nId = j ; + break ; + } + } + m_mTree[nId].m_vInters.emplace_back() ; + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( ptInters.y > m_mTree[nId].GetBottomLeft().y + EPS_SMALL && ptInters.y < m_mTree[nId].GetTopRight().y - EPS_SMALL) + m_mTree[nId].m_vInters.back().nIn = 3 ; + else if ( AreSamePointApprox( ptInters, ptBr)) + m_mTree[nId].m_vInters.back().nIn = 6 ; + else if ( AreSamePointApprox( ptInters, m_mTree[nId].GetTopRight())) + m_mTree[nId].m_vInters.back().nIn = 7 ; + else + m_mTree[nId].m_vInters.back().nIn = 3 ; + } + else if ( nEdge == 2) { + GetBottomNeigh( nId, vNeigh) ; + for ( int j : vNeigh) { + if ( ptInters.x <= m_mTree[j].GetTopRight().x) { + nId = j ; + break ; + } + } + m_mTree[nId].m_vInters.emplace_back() ; + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + if ( ptInters.x > m_mTree[nId].GetBottomLeft().x + EPS_SMALL && ptInters.x < m_mTree[nId].GetTopRight().x - EPS_SMALL) + m_mTree[nId].m_vInters.back().nIn = 0 ; + else if ( AreSamePointApprox( ptInters, ptTl)) + m_mTree[nId].m_vInters.back().nIn = 4 ; + else if ( AreSamePointApprox( ptInters, m_mTree[nId].GetTopRight())) + m_mTree[nId].m_vInters.back().nIn = 7 ; + else + m_mTree[nId].m_vInters.back().nIn = 0 ; + } + else if ( nEdge == 3) { + GetRightNeigh( nId, vNeigh) ; + for ( int j : vNeigh) { + if ( ptInters.y <= m_mTree[j].GetTopRight().y) { + nId = j ; + break ; + } + } + m_mTree[nId].m_vInters.emplace_back() ; + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + if ( ptInters.y > m_mTree[nId].GetBottomLeft().y + EPS_SMALL && ptInters.y < m_mTree[nId].GetTopRight().y - EPS_SMALL) + m_mTree[nId].m_vInters.back().nIn = 1 ; + else if ( AreSamePointApprox( ptInters, m_mTree[nId].GetBottomLeft())) + m_mTree[nId].m_vInters.back().nIn = 5 ; + else if ( AreSamePointApprox( ptInters, ptTl)) + m_mTree[nId].m_vInters.back().nIn = 4 ; + else + m_mTree[nId].m_vInters.back().nIn = 1 ; + } + // esco da uno dei vertici + else if ( nEdge == 4) { + GetTopNeigh( nId, vNeigh) ; + GetLeftNeigh( nId, vNeigh1) ; + INTVECTOR nPossible, nPossible1 ; + nPossible = FindCell( ptInters, clTrim, vNeigh) ; + nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ; + // ingresso dal basso + if ( ! nPossible.empty()) { + nId = nPossible[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( AreSamePointExact( ptInters, ptBr)) + m_mTree[nId].m_vInters.back().nIn = 6 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetBottomLeft())) + m_mTree[nId].m_vInters.back().nIn = 5 ; + else + m_mTree[nId].m_vInters.back().nIn = 2 ; + } + // ingresso da destra + else if ( ! nPossible1.empty()) { + nId = nPossible1.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( AreSamePointExact( ptInters, ptBr)) + m_mTree[nId].m_vInters.back().nIn = 6 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetTopRight())) + m_mTree[nId].m_vInters.back().nIn = 7 ; + else + m_mTree[nId].m_vInters.back().nIn = 3 ; + } + // ingresso in diagonale + else { + if ( ! vNeigh.empty()) { + int nIdTemp = vNeigh[0] ; + vNeigh.clear() ; + GetLeftNeigh( nIdTemp, vNeigh) ; + nId = vNeigh[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 6 ; + } + else { + nId = vNeigh1.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 7 ; + } + } + } + else if ( nEdge == 5) { + GetLeftNeigh( nId, vNeigh) ; + GetBottomNeigh( nId, vNeigh1) ; + INTVECTOR nPossible, nPossible1 ; + nPossible = FindCell( ptInters, clTrim, vNeigh) ; + nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ; + // ingresso dal destra + if ( ! nPossible.empty()) { + nId = nPossible[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( AreSamePointExact( ptInters, ptBr)) + m_mTree[nId].m_vInters.back().nIn = 6 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetTopRight())) + m_mTree[nId].m_vInters.back().nIn = 7 ; + else + m_mTree[nId].m_vInters.back().nIn = 3 ; + } + // ingresso dall'alto + else if ( ! nPossible1.empty()) { + nId = nPossible1[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + if ( AreSamePointExact( ptInters, ptTl)) + m_mTree[nId].m_vInters.back().nIn = 4 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetTopRight())) + m_mTree[nId].m_vInters.back().nIn = 7 ; + else + m_mTree[nId].m_vInters.back().nIn = 0 ; + } + // ingresso in diagonale + else { + if ( ! vNeigh.empty()) { + int nIdTemp = vNeigh[0] ; + vNeigh.clear() ; + GetBottomNeigh( nIdTemp, vNeigh) ; + nId = vNeigh.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 7 ; + } + else { + nId = vNeigh1[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 4 ; + } + } + } + else if ( nEdge == 6) { + GetBottomNeigh( nId, vNeigh) ; + GetRightNeigh( nId, vNeigh1) ; + INTVECTOR nPossible, nPossible1 ; + nPossible = FindCell( ptInters, clTrim, vNeigh) ; + nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ; + // ingresso dall'alto + if ( ! nPossible.empty()) { + nId = nPossible.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + if ( AreSamePointExact( ptInters, ptTl)) + m_mTree[nId].m_vInters.back().nIn = 4 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetTopRight())) + m_mTree[nId].m_vInters.back().nIn = 7 ; + else + m_mTree[nId].m_vInters.back().nIn = 0 ; + } + // ingresso da sinistra + else if ( ! nPossible1.empty()) { + nId = nPossible1[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + if ( AreSamePointExact( ptInters, ptTl)) + m_mTree[nId].m_vInters.back().nIn = 4 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetBottomLeft())) + m_mTree[nId].m_vInters.back().nIn = 5 ; + else + m_mTree[nId].m_vInters.back().nIn = 1 ; + } + // ingresso in diagonale + else { + if ( ! vNeigh.empty()){ + int nIdTemp = vNeigh.back() ; + vNeigh.clear() ; + GetRightNeigh( nIdTemp, vNeigh) ; + nId = vNeigh.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 4 ; + } + else { + nId = vNeigh1[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 5 ; + } + } + } + else if ( nEdge == 7) { + GetRightNeigh( nId, vNeigh) ; + GetTopNeigh( nId, vNeigh1) ; + INTVECTOR nPossible, nPossible1 ; + nPossible = FindCell( ptInters, clTrim, vNeigh) ; + nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ; + // ingresso da sinistra + if ( ! nPossible.empty()) { + nId = nPossible.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + if ( AreSamePointExact( ptInters, ptTl)) + m_mTree[nId].m_vInters.back().nIn = 4 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetBottomLeft())) + m_mTree[nId].m_vInters.back().nIn = 5 ; + else + m_mTree[nId].m_vInters.back().nIn = 1 ; + } + // ingresso dal basso + else if ( ! nPossible1.empty()) { + nId = nPossible1.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + // controllo se entro in un vertice o a metà lato + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( AreSamePointExact( ptInters, ptBr)) + m_mTree[nId].m_vInters.back().nIn = 6 ; + else if ( AreSamePointExact( ptInters, m_mTree[nId].GetBottomLeft())) + m_mTree[nId].m_vInters.back().nIn = 5 ; + else + m_mTree[nId].m_vInters.back().nIn = 2 ; + } + // ingresso in diagonale + else { + if ( ! vNeigh.empty() /*&& ! m_mTree[vNeigh[0]].m_bOnLeftEdge */) { // questa aggiunta in teoria non serve + int nIdTemp = vNeigh.back() ; + vNeigh.clear() ; + GetTopNeigh( nIdTemp, vNeigh) ; + nId = vNeigh[0] ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 5 ; + } + else { + nId = vNeigh1.back() ; + m_mTree[nId].m_vInters.emplace_back() ; + m_mTree[nId].m_vInters.back().nIn = 6 ; + } + } + } + // aggiungo l'intersezione al vettore delle intersezioni della prossima cella + vptInters.push_back( ptInters) ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::CreateCellPolygons( const int& nLeafId, std::vector& vPolygons, INTVECTOR& vToCheck, int& nPoly, INTVECTOR& vnParentChunk, const PolyLine& plCell) +{ + // conto quanti vertici in più ho per lato e creo un vettore dei vertici per lato + int nId = m_vnLeaves[nLeafId] ; + std::vector vEdgeVertex ; + Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + vEdgeVertex.emplace_back() ; + vEdgeVertex.back().push_back( m_mTree[nId].GetTopRight()) ; + vEdgeVertex.emplace_back() ; + vEdgeVertex.back().push_back( ptTl) ; + vEdgeVertex.emplace_back() ; + vEdgeVertex.back().push_back( m_mTree[nId].GetBottomLeft()) ; + vEdgeVertex.emplace_back() ; + vEdgeVertex.back().push_back( ptBr) ; + // la PolyLine è riempita a partire dal lato bottom + Point3d ptStart ; + plCell.GetFirstPoint( ptStart) ; + INTVECTOR vEdge = { 2, 3, 0, 1} ; + for ( int p = 0 ; p < 4 ; ++ p) { + int j = vEdge[p] ; + int next = j +1 ; + if ( j == 3) + next = 0 ; + Point3d ptToAdd ; + while ( plCell.GetNextPoint( ptToAdd) && ! AreSamePointExact( ptToAdd, vEdgeVertex[next][0])) { + vEdgeVertex[j].push_back( ptToAdd) ; + } + } + + // comincio a costruire il poligono + INTVECTOR vToCheckNow = vToCheck ; + // vettore dei poligoni ( loop) della cella nId + POLYLINEVECTOR vCellPolygons ; + // costruisco i poligoni partendo dal vettore delle intersezioni, come spiegato a pag15 di Cripps + PolyLine plTrimmedPoly ; + // numero di volte che la cella è stata attraversata da una curva di trim + int nPassToCheck = (int) vToCheckNow.size() ; + // numero di vertici aggiunti al nuovo poligono + int c = 0 ; + // scorro sui vettori intersezione della cella nId e sui suoi vertici + // in questo for analizzo solo i loop che tagliano la cella + int nEdgeIn = -1 ; + int nFirstLoopInPoly = -1 ; + INTVECTOR vAddedLoops ; + for ( int w = 0 ; w < (int)vToCheckNow.size() ; ++ w) { + if ( m_mTree[nId].m_vInters[w].vpt.size() < 2) { + continue ; + } + // indice del loop in m_vInters + int j = vToCheckNow[w] ; + Inters inA = m_mTree[nId].m_vInters[j] ; + if ( inA.nIn != -1) { + int nEdge ; + if ( nEdgeIn == -1) { + // salvo il lato di ingresso del primo lato del poligono che sto costruendo + nEdgeIn = inA.nIn ; + nFirstLoopInPoly = j ; + } + for ( Point3d ptInt : inA.vpt) { + AddVertex( nId, vEdgeVertex, plTrimmedPoly, c, ptInt) ; + } + vAddedLoops.push_back( j) ; + nEdge = inA.nOut ; + // devo verificare di non essere uscito in un vertice, con un tratto sovrapposto al lato che ripercorrerò tra poco + Point3d ptLast, ptSecondToLast ; + plTrimmedPoly.GetLastPoint( ptLast) ; + plTrimmedPoly.GetPrevPoint( ptSecondToLast) ; + Vector3d vLast = ptLast - ptSecondToLast ; + vLast.Normalize() ; + Vector3d vEdge ; + if ( AreSameEdge( nEdge, 0)) { + if ( AreSamePointExact( ptLast, ptTl)) { + vEdge = m_mTree[nId].GetBottomLeft() - ptTl ; + vEdge.Normalize() ; + if ( AreOppositeVectorApprox( vLast, vEdge)) { + plTrimmedPoly.EraseLastUPoint() ; + nEdge = 1 ; + } + } + } + else if ( AreSameEdge( nEdge, 1) && AreSamePointExact( ptLast, m_mTree[nId].GetBottomLeft())) { + vEdge = ptBr - m_mTree[nId].GetBottomLeft() ; + vEdge.Normalize() ; + if ( AreOppositeVectorApprox( vLast, vEdge)) { + plTrimmedPoly.EraseLastUPoint() ; + nEdge = 2 ; + } + } + else if ( AreSameEdge( nEdge, 2)) { + if ( AreSamePointExact( ptLast, ptBr)) { + vEdge = m_mTree[nId].GetTopRight() - ptBr ; + vEdge.Normalize() ; + if ( AreOppositeVectorApprox( vLast, vEdge)) { + plTrimmedPoly.EraseLastUPoint() ; + nEdge = 3 ; + } + } + } + else if ( AreSameEdge( nEdge, 3) && AreSamePointExact( ptLast, m_mTree[nId].GetTopRight())) { + vEdge = ptTl - m_mTree[nId].GetTopRight() ; + vEdge.Normalize() ; + if ( AreOppositeVectorApprox( vLast, vEdge)) { + plTrimmedPoly.EraseLastUPoint() ; + nEdge = 0 ; + } + } + + // se mi è rimasto solo un punto sulla polyline vuol dire che avevo solo un tratto parallelo ad lato + // quindi salto al prossimo loop + if ( plTrimmedPoly.GetPointNbr() == 1) { + plTrimmedPoly.Clear() ; + if ( j == nFirstLoopInPoly) + nEdgeIn = -1 ; + continue ; + } + if ( nEdge > 3 && nEdge != 7) { + nEdge = nEdge - 3 ; + } + else if ( nEdge == 7) { + nEdge = 0 ; + } + // se ho altri Pass vado avanti ad aggiungere vertici finché trovo il prossimo o finché non sono tornato sul lato di partenza + bool bNotCameBack = true ; + bool bValidNextStart = false ; + bool bAtNextStart = false ; + if ( w < nPassToCheck - 1) { + int nSecondCheck = 0 ; + int nNext ; + // ciclo sui loop successivi per vedere se ne ho uno con un valid start + for ( int t = w + 1 ; t < nPassToCheck ; ++ t) { + bValidNextStart = CheckIfBetween( m_mTree[nId].m_vInters[j], m_mTree[nId].m_vInters[vToCheckNow[t]]) ; + if ( bValidNextStart) { + nNext = t ; + bAtNextStart = AreSameEdge( m_mTree[nId].m_vInters[j].nOut, m_mTree[nId].m_vInters[vToCheckNow[t]].nIn) ; + break ; + } + else { + ++ nSecondCheck ; + } + } + + bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ; + + while ( ! ( bValidNextStart && bAtNextStart) && bNotCameBack) { + Point3d ptVert ; + if ( nEdge == 0) + ptVert = ptTl ; + else if ( nEdge == 1) + ptVert = m_mTree[nId].GetBottomLeft() ; + else if ( nEdge == 2) + ptVert = ptBr ; + else if ( nEdge == 3) + ptVert = m_mTree[nId].GetTopRight() ; + AddVertex( nId, vEdgeVertex, plTrimmedPoly, c, ptVert) ; + if ( nEdge > 3 && nEdge != 7) + nEdge = nEdge - 4 ; + else if ( nEdge < 3) + ++ nEdge ; + else + nEdge = 0 ; + + // aggiorno le condizioni per il while + if ( bValidNextStart) + bAtNextStart = AreSameEdge( nEdge, m_mTree[nId].m_vInters[vToCheckNow[nNext]].nIn) ; + + bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ; + } + + // se ho trovato un altro loop salto all'inizio del for, dopo aver aggiunto eventuali punti intermedi + if ( bValidNextStart) { + for ( int p = 1 ; p < (int) vEdgeVertex[nEdge].size() ; ++ p) { + if ( CheckIfBefore( nEdge, vEdgeVertex[nEdge][p], m_mTree[nId].m_vInters[vToCheckNow[nNext]].vpt[0])) { + plTrimmedPoly.AddUPoint( c, vEdgeVertex[nEdge][p]) ; + ++ c ; + } + } + w = w + nSecondCheck ; + continue ; + } + // sono tornato indietro + else if ( ! bNotCameBack){ + Point3d ptStart ; + plTrimmedPoly.GetFirstPoint( ptStart) ; + for ( int p = 1 ; p < (int) vEdgeVertex[nEdge].size() ; ++ p) { + if ( CheckIfBefore( nEdge, vEdgeVertex[nEdge][p], ptStart)) { + plTrimmedPoly.AddUPoint( c, vEdgeVertex[nEdge][p]) ; + ++ c ; + } + } + } + } + // non ho altri loop quindi aggiungo vertici finché torno al punto di partenza + else { + bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ; + + Point3d ptStart ; + plTrimmedPoly.GetFirstPoint( ptStart) ; + while ( bNotCameBack) { + Point3d ptVert ; + if ( nEdge == 0) + ptVert = ptTl ; + else if ( nEdge == 1) + ptVert = m_mTree[nId].GetBottomLeft() ; + else if ( nEdge == 2) + ptVert = ptBr ; + else if ( nEdge == 3) + ptVert = m_mTree[nId].GetTopRight() ; + AddVertex( nId, vEdgeVertex, plTrimmedPoly, c, ptVert) ; + if ( nEdge > 3 && nEdge != 7) + nEdge = nEdge - 4 ; + else if ( nEdge < 3) + ++ nEdge ; + else + nEdge = 0 ; + // aggiorno le condizioni per il while + bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ; + } + for ( int p = 1 ; p < (int) vEdgeVertex[nEdge].size() ; ++ p) { + if ( CheckIfBefore( nEdge, vEdgeVertex[nEdge][p], ptStart)) { + plTrimmedPoly.AddUPoint( c, vEdgeVertex[nEdge][p]) ; + ++ c ; + } + } + + } + plTrimmedPoly.Close() ; + // controllo sull'area del poligono, se è 0 ( quindi un segmento), non lo aggiungo + double dArea ; + plTrimmedPoly.GetAreaXY( dArea) ; + //if ( dArea > SQ_EPS_SMALL) { + if ( dArea > 0) { + vCellPolygons.push_back( plTrimmedPoly) ; + vPolygons.push_back( vCellPolygons) ; + ++ nPoly ; + vnParentChunk.push_back( inA.nChunk) ; + vCellPolygons.clear() ; + } + + c = 0 ; + plTrimmedPoly.Clear() ; + nEdgeIn = -1 ; + // devo verificare se tra i loop che sono finiti in vToCheck in realtà qualcuno l'ho usato per fare un poligono + for ( int k = 0 ; k < (int)vToCheck.size() ; ++ k) { + for ( int i = 0 ; i < (int)vAddedLoops.size() ; ++ i) { + if ( vToCheck[k] == vAddedLoops[i]) { + vToCheck.erase( vToCheck.begin() + k) ; + } + } + } + } + else + continue ; + } + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::CreateIslandAndHoles( const int& nLeafId, std::vector& vPolygons, int& nPoly, INTVECTOR& vnParentChunk) +{ + // vettore dei poligoni ( loop) della cella nId + POLYLINEVECTOR vCellPolygons ; + // costruisco i poligoni partendo dal vettore delle intersezioni + int nId = m_vnLeaves[nLeafId] ; + PolyLine plTrimmedPoly ; + + // loop interni in una cella intersecata + int nChunkBiggestCW = -1 ; + if ( m_mTree[nId].m_nFlag == 3 || m_mTree[nId].m_nFlag == 2) { + PolyLine plInLoop ; + Inters inA ; + // se ho almeno un loop CW che non è contenuto in un altro poligono o in un loop interno CCW devo aggiungere il bordo + bool bAllContained = true ; + bool bContained = false ; + int nInters = (int) m_mTree[nId].m_vInters.size() ; + for ( int n = 0 ; n < nInters ; ++ n) { + inA = m_mTree[nId].m_vInters[n] ; + if ( inA.nIn == -1) { + // per ogni loop CW verifico che ci sia un loop CCW dello stesso chunk ( che quindi lo contiene) + if ( ! inA.bCCW) { + if ( nChunkBiggestCW == -1) + nChunkBiggestCW = inA.nChunk ; + bContained = false ; + Inters inB = m_mTree[nId].m_vInters[0] ; + for( int c = 0 ; c < nInters ; ++ c){ + inB = m_mTree[nId].m_vInters[c] ; + if ( inB.nIn == -1) { + if ( inB != inA && inB.nChunk == inA.nChunk && inB.bCCW) { + bContained = true ; + break ; + } + } + else + break ; + } + bAllContained = bAllContained && bContained ; + } + } + else + break ; + } + + if ( m_mTree[nId].m_nFlag == 2 && ! bAllContained) { + // i loop esterni sono CW, quindi prima dei loop di trim aggiungo il bordo cella + Point3d ptVert = m_mTree[nId].GetTopRight() ; + plInLoop.AddUPoint( 0, ptVert) ; + ptVert.x = m_mTree[nId].GetBottomLeft().x ; + ptVert.y = m_mTree[nId].GetTopRight().y ; + plInLoop.AddUPoint( 1, ptVert) ; + ptVert = m_mTree[nId].GetBottomLeft() ; + plInLoop.AddUPoint( 2, ptVert) ; + ptVert.x = m_mTree[nId].GetTopRight().x ; + ptVert.y = m_mTree[nId].GetBottomLeft().y ; + plInLoop.AddUPoint( 3, ptVert) ; + plInLoop.Close() ; + vCellPolygons.push_back( plInLoop) ; + vPolygons.push_back( vCellPolygons) ; + ++ nPoly ; + // imposto il chunk del loop CW più grande ( il primo che ho incontrato) + vnParentChunk.push_back( nChunkBiggestCW) ; + vCellPolygons.clear() ; + plInLoop.Clear() ; + } + for ( int nLoop = 0 ; nLoop < nInters ; ++ nLoop) { + inA = m_mTree[nId].m_vInters[nLoop] ; + if ( inA.nIn == -1) { + // numero di vertici aggiunti al nuovo poligono + int k = 0 ; + for ( Point3d ptInt : inA.vpt) { + plInLoop.AddUPoint( k, ptInt) ; + ++ k ; + } + plInLoop.Close() ; + bool bAdded = false ; + // se il loop è CW devo controllare in quale altro dei poligoni che ho già aggiunto è contenuto + if ( ! inA.bCCW) { + Point3d ptStart ; + plInLoop.GetFirstPoint( ptStart) ; + int nOtherPoly = (int)vPolygons.size() ; + for ( int r = 0 ; r < nPoly ; ++r) { + if ( IsPointInsidePolyLine( ptStart, vPolygons[nOtherPoly - r - 1][0], -0.01) && vnParentChunk[nPoly - r - 1] == inA.nChunk) { + vPolygons[nOtherPoly - r - 1].push_back( plInLoop) ; + plInLoop.Clear() ; + bAdded = true ; + break ; + } + } + } + if ( ! bAdded) { + vCellPolygons.push_back( plInLoop) ; + vPolygons.push_back( vCellPolygons) ; + ++ nPoly ; + vnParentChunk.push_back( inA.nChunk) ; + plInLoop.Clear() ; + vCellPolygons.clear() ; + } + plInLoop.Clear() ; + } + else + break ; + } + } + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::CheckIfBefore( const PolyLine& pl, const int& nEdge) const +{ + // controllo se ptEnd è prima di ptStart sul lato nEdge rispetto al senso antiorario ( quindi se è dopo in senso orario) + Point3d ptStart, ptEnd ; + pl.GetFirstPoint( ptStart) ; + pl.GetLastPoint( ptEnd) ; + if ( AreSameEdge( nEdge, 0)) { + return ptEnd.x > ptStart.x ; + } + else if ( AreSameEdge( nEdge, 1)) { + return ptEnd.y > ptStart.y ; + } + else if ( AreSameEdge( nEdge, 2)) { + return ptEnd.x < ptStart.x ; + } + else if ( AreSameEdge( nEdge, 3)) { + return ptEnd.y < ptStart.y ; + } + return false ; +} + +//---------------------------------------------------------------------------- +bool +Tree::CheckIfBefore( const Inters& inA) const +{ + // questa funzione è pensata in riferimento al lato 3, quindi nessuno dei due punti può stare su Edge = 3 + // controllo se l'ingresso è prima dell'uscita + int nEdge1 = inA.nIn ; + int nEdge2 = inA.nOut ; + if ( nEdge1 == -1) + return false ; + PolyLine pl ; + pl.AddUPoint( 0, inA.vpt.back()) ; + pl.AddUPoint( 1, inA.vpt[0]) ; + INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6} ; + // controllo se nEdge1 viene prima di nEdge2. la partenza è da ptTR e l'arrivo è ptBr + + INTVECTOR :: iterator iter1 = find( vEdges.begin(), vEdges.end(), nEdge1) ; + int nPos1 = std::distance( vEdges.begin(), iter1) ; + INTVECTOR :: iterator iter2 = find( vEdges.begin(), vEdges.end(), nEdge2) ; + int nPos2 = std::distance( vEdges.begin(), iter2) ; + if ( nPos1 < nPos2) + return true ; + else if ( nPos1 > nPos2) + return false ; + // nPos1 == nPos2 + else { + if ( CheckIfBefore( pl, vEdges[nPos1])) { + return true ; + } + else + return false ; + } +} + +//---------------------------------------------------------------------------- +bool +Tree::CheckIfBefore( const int& nEdge1, const Point3d& ptP1, const int& nEdge2, const Point3d& ptP2) const +{ + if ( nEdge1 == -1 || nEdge2 == -1) + return false ; + // questa funzione è pensata in riferimento al lato 3, quindi nessuno dei due punti può stare su Edge = 3 + INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6} ; + // controllo se ptP1, che è su nEdge1, viene prima di ptP2, che è su nEdge2. la partenza è da ptTR e l'arrivo è ptBr + INTVECTOR :: iterator iter1 = find( vEdges.begin(), vEdges.end(), nEdge1) ; + int nPos1 = std::distance( vEdges.begin(), iter1) ; + INTVECTOR :: iterator iter2 = find( vEdges.begin(), vEdges.end(), nEdge2) ; + int nPos2 = std::distance( vEdges.begin(), iter2) ; + if ( nPos1 < nPos2) + return true ; + else if ( nPos1 > nPos2) + return false ; + // ( nPos1 == nPos2) + else { + PolyLine pl ; + pl.AddUPoint( 0, ptP2) ; + pl.AddUPoint( 1, ptP1) ; + if ( CheckIfBefore( pl, vEdges[nPos1])) + return true ; + else + return false ; + } +} + +//---------------------------------------------------------------------------- +bool +Tree::CheckIfBefore( const int& nEdge, const Point3d& ptP1, const Point3d& ptP2, const int& nEdge2) const +{ + // i punti devono essere sullo stesso lato nEdge + // nEdge2 è di backup, in caso nEdge sia un vertice, per capire di quale lato si tratta + int nEdgeRef ; + if ( nEdge != nEdge2 && nEdge2 != -1) { + if ( std::min(nEdge, nEdge2) < 4) + nEdgeRef = std::min(nEdge, nEdge2) ; + else if ( AreSameEdge( nEdge, 0) && AreSameEdge( nEdge2, 0)) + nEdgeRef = 0 ; + else if ( AreSameEdge( nEdge, 1) && AreSameEdge( nEdge2, 1)) + nEdgeRef = 1 ; + else if ( AreSameEdge( nEdge, 2) && AreSameEdge( nEdge2, 2)) + nEdgeRef = 2 ; + else if ( AreSameEdge( nEdge, 3) && AreSameEdge( nEdge2, 3)) + nEdgeRef = 3 ; + } + else + nEdgeRef = nEdge ; + // sul lato nEdge controllo se ptP1 viene prima di ptP2. + // i lati vengono percorsi in senso antiorario + if ( AreSameEdge( nEdgeRef, 0)) { + return ptP1.x > ptP2.x ; + } + else if ( AreSameEdge( nEdgeRef, 1)) { + return ptP1.y > ptP2.y ; + } + else if ( AreSameEdge( nEdgeRef, 2)) { + return ptP1.x < ptP2.x ; + } + else if ( AreSameEdge( nEdgeRef, 3)) { + return ptP1.y < ptP2.y ; + } + return false ; +} + +//---------------------------------------------------------------------------- +bool +Tree::AreSameEdge( const int& nEdge1, const int nEdge2) const +{ + if ( nEdge1 == 0) { + if ( nEdge2 == 4 || nEdge2 == 0 || nEdge2 == 7) + return true ; + else + return false ; + } + else if ( nEdge1 == 1) { + if ( nEdge2 == 4 || nEdge2 == 1 || nEdge2 == 5) + return true ; + else + return false ; + } + else if ( nEdge1 == 2) { + if ( nEdge2 == 6 || nEdge2 == 2 || nEdge2 == 5) + return true ; + else + return false ; + } + else if ( nEdge1 == 3) { + if ( nEdge2 == 6 || nEdge2 == 3 || nEdge2 == 7) + return true ; + else + return false ; + } + else if ( nEdge1 == 4) { + if ( nEdge2 == 0 || nEdge2 == 1 || nEdge2 == 7 || nEdge2 == 5) + return true ; + else + return false ; + } + else if ( nEdge1 == 5) { + if ( nEdge2 == 2 || nEdge2 == 1 || nEdge2 == 4 || nEdge2 == 6) + return true ; + else + return false ; + } + else if ( nEdge1 == 6) { + if ( nEdge2 == 2 || nEdge2 == 3 || nEdge2 == 5 || nEdge2 == 7) + return true ; + else + return false ; + } + else if ( nEdge1 == 7) { + if ( nEdge2 == 0 || nEdge2 == 3 || nEdge2 == 4 || nEdge2 == 6) + return true ; + else + return false ; + } + else + return false ; +} + +//---------------------------------------------------------------------------- +bool +Tree::AddVertex( const int& nId, const std::vector& vEdgeVertex, PolyLine& plTrimmedPoly, int& c, const Point3d& ptToAdd) const +{ + // se è il primo punto della PolyLine lo aggiungo + if ( plTrimmedPoly.GetPointNbr() == 0) { + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + return true ; + } + Point3d ptBr = vEdgeVertex[3][0] ; + Point3d ptTR = vEdgeVertex[0][0] ; + Point3d ptTl = vEdgeVertex[1][0] ; + Point3d ptBL = vEdgeVertex[2][0] ; + Point3d ptLast ; + plTrimmedPoly.GetLastPoint( ptLast) ; + // verifico di essere allineato con un lato, sennò aggiungo e basta + Vector3d vDir ; + if ( ! AreSamePointApprox( ptToAdd, ptLast)) + vDir = ptToAdd - ptLast ; + else + return true ; + // se non riesco a normalizzare perché sono troppo vicino ad un vertice allora aggiungo direttamente il vertice + if ( ! vDir.Normalize()) { + plTrimmedPoly.EraseLastUPoint() ; + if ( AreSamePointApprox( ptToAdd, ptBr)) + plTrimmedPoly.AddUPoint( c, ptBr) ; + else if ( AreSamePointApprox( ptToAdd, ptTR)) + plTrimmedPoly.AddUPoint( c, ptTR) ; + else if ( AreSamePointApprox( ptToAdd, ptTl)) + plTrimmedPoly.AddUPoint( c, ptTl) ; + else if ( AreSamePointApprox( ptToAdd, ptBL)) + plTrimmedPoly.AddUPoint( c, ptBL) ; + else + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + return true ; + } + if ( abs( vDir.x) > 1 - EPS_SMALL || abs( vDir.y) > 1 - EPS_SMALL) { + // se su un edge devo fare dei controlli + // edge 0 + if ( ptToAdd.x >= ptBL.x && ptToAdd.x <= ptTR.x && ptToAdd.y == ptTR.y && abs( vDir.x) > 1 - EPS_SMALL) { + for ( int t = 0 ; t < (int)vEdgeVertex[0].size() ; ++ t) { + Point3d ptIntermed = vEdgeVertex[0][t] ; + if ( ptIntermed.x > ptToAdd.x && ptIntermed.x < ptLast.x) { + plTrimmedPoly.AddUPoint( c, ptIntermed) ; + ++ c ; + } + } + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + } + // edge 1 + else if ( ptToAdd.y >= ptBL.y && ptToAdd.y <= ptTR.y && ptToAdd.x == ptBL.x && abs( vDir.y) > 1 - EPS_SMALL) { + for ( int t = 0 ; t < (int)vEdgeVertex[1].size() ; ++ t) { + Point3d ptIntermed = vEdgeVertex[1][t] ; + if ( ptIntermed.y > ptToAdd.y && ptIntermed.y < ptLast.y) { + plTrimmedPoly.AddUPoint( c, ptIntermed) ; + ++ c ; + } + } + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + } + // edge 2 + else if ( ptToAdd.x >= ptBL.x && ptToAdd.x <= ptTR.x && ptToAdd.y == ptBL.y && abs( vDir.x) > 1 - EPS_SMALL) { + for ( int t = 0 ; t < (int)vEdgeVertex[2].size() ; ++ t) { + Point3d ptIntermed = vEdgeVertex[2][t] ; + if ( ptIntermed.x < ptToAdd.x && ptIntermed.x > ptLast.x) { + plTrimmedPoly.AddUPoint( c, ptIntermed) ; + ++ c ; + } + } + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + } + // edge 3 + else if ( ptToAdd.y >= ptBL.y && ptToAdd.y <= ptTR.y && ptToAdd.x == ptTR.x && abs( vDir.y) > 1 - EPS_SMALL) { + for ( int t = 0 ; t < (int)vEdgeVertex[3].size() ; ++ t) { + Point3d ptIntermed = vEdgeVertex[3][t] ; + if ( ptIntermed.y < ptToAdd.y && ptIntermed.y > ptLast.y) { + plTrimmedPoly.AddUPoint( c, ptIntermed) ; + ++ c ; + } + } + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + } + // sono allineato con un lato, ma NON sono su un lato + // aggiungo e basta + else { + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + } + } + // non su un edge, quindi aggiungo e basta + else { + plTrimmedPoly.AddUPoint( c, ptToAdd) ; + ++ c ; + } + return true ; +} + +//usando i poligoni // deprecato e non funzionante +////---------------------------------------------------------------------------- +//bool +//Tree::SetRightEdgeIn( int nId, std::vector& vEdgeVertex, PolyLine& plTrimmedPoly) +//{ +// Point3d ptBr( m_mTree.at( nId).GetTopRight().x , m_mTree.at( nId).GetBottomLeft().y) ; +// Point3d ptTR = m_mTree.at( nId).GetTopRight() ; +// int nPos0 = -1 , nPos1 = -1 ; +// bool bSave = false ; +// PNTLIST lPtInt ; +// for ( int k = 0 ; k < plTrimmedPoly.GetPointNbr() ; ++ k) { +// Point3d ptIter ; +// plTrimmedPoly. GetNextPoint( ptIter) ; +// if ( AreSamePointExact( ptIter, ptBr)) { +// nPos0 = k ; +// bSave = bSave ^ true ; +// } +// if ( AreSamePointExact( ptIter, ptTR)) { +// nPos1 = k ; +// bSave = bSave ^ true ; +// } +// if ( bSave) { +// lPtInt.push_back( ptIter) ; +// } +// } +// lPtInt.pop_front() ; +// // verifico se ci sono tutti e due i punti +// if ( nPos0 != -1 && nPos1 != -1) { +// // se sono di seguito tutto il lato destro è dentro +// if ( nPos1 == nPos0 + 1 || nPos0 == nPos1 + 1) { +// m_mTree[nId].m_nRightEdgeIn = 1 ; +// } +// // ci sono dei punti in mezzo, devo capire se sono allineati o no +// else { +// // verifico se tutti i punti intermedi sono allineati o no +// CurveLine clRightEdge ; +// clRightEdge.Set( ptBr, ptTR) ; +// bool bAllOn = true ; +// +// PNTLIST::iterator lIter = lPtInt.begin() ; +// std::advance( lIter, 0) ; +// for ( int p = 0 ; p < (int)lPtInt.size() ; ++ p) { +// if ( ! clRightEdge.IsPointOn( *lIter)) { +// bAllOn = false ; +// break ; +// } +// } +// // divisi da punti allineati e quindi lato dentro +// if ( bAllOn) { +// m_mTree[nId].m_nRightEdgeIn = 1 ; +// } +// // se sono dentro, ma non allineati, allora solo in parte +// else { +// m_mTree[nId].m_nRightEdgeIn = 2 ; +// } +// } +// } +// // il lato destro è sicuramente esterno +// else { +// m_mTree[nId].m_nRightEdgeIn = 0 ; +// } +// return true ; +//} + +//usando le intersezioni con le celle +//---------------------------------------------------------------------------- +bool +Tree::SetRightEdgeIn( const int& nId) +{ + // categorizzo la cella in base a quanta parte del lato destro è conenuta all'interno delle curve di trim + // RightEdgeIn -> 0 non contenuto ; 1 contenuto ; 2 in parte contenuto + int nPass = (int) m_mTree[nId].m_vInters.size() ; + if ( nPass == 0 ) { + m_mTree[nId].m_nRightEdgeIn = 0 ; + return true ; + } + bool bDone = false ; + // se ho solo loop interni devo controllare se il più esterno è CCW ( lato destro esterno) o CW ( lato destro interno) + if ( m_mTree[nId].m_nFlag == 2) { + bool bAllContained = true ; + bool bContained = false ; + Inters inA ; + int nInters = (int) m_mTree[nId].m_vInters.size() ; + for ( int n = 0 ; n < nInters ; ++ n) { + inA = m_mTree[nId].m_vInters[n] ; + if ( inA.nIn == -1) { + if ( ! inA.bCCW) { + bContained = false ; + Inters inB = m_mTree[nId].m_vInters[0] ; + for( int c = 0 ; c < nInters ; ++ c){ + inB = m_mTree[nId].m_vInters[c] ; + if ( inB.nIn == -1) { + if ( inB != inA && inB.nChunk == inA.nChunk && inB.bCCW) { + bContained = true ; + break ; + } + } + else + break ; + } + bAllContained = bAllContained && bContained ; + } + } + else + break ; + } + // se i loop CW interni sono tutti contenuti in un loop CW allora il right edge è esterno + bool bRightEdgeIn = ! bAllContained ; + if ( bRightEdgeIn) + m_mTree[nId].m_nRightEdgeIn = 1 ; + else + m_mTree[nId].m_nRightEdgeIn = 0 ; + return true ; + } + // se ho inters sul lato destro ( nEdge == 3) allora in parte è dentro + for ( int k = 0 ; k < nPass ; ++ k) { + if ( m_mTree[nId].m_vInters[k].nIn == 3 || m_mTree[nId].m_vInters[k].nOut == 3) { + m_mTree[nId].m_nRightEdgeIn = 2 ; + bDone = true ; + break ; + } + // considero anche ingressi/ uscite dai vertici 6 e 7 + // controllo nei vertici + else if ( m_mTree[nId].m_vInters[k].nOut == 6 && m_mTree[nId].m_vInters[k].nIn == 7) { + m_mTree[nId].m_nRightEdgeIn = 1 ; + bDone = true ; + break ; + } + else if ( m_mTree[nId].m_vInters[k].nOut == 7 && m_mTree[nId].m_vInters[k].nIn == 6) { + m_mTree[nId].m_nRightEdgeIn = 0 ; + bDone = true ; + break ; + } + } + // se non ho inters sul lato destro devo verificare se è tutto dentro o tutto fuori + if ( ! bDone) { + // ciclo sulle inters e cerco quella più a destra, se ha un'uscita più bassa di un'entrata allora il right edge è compreso! + bool bFound = false ; + bool bRightMost = false ; + int nEdgeUp = 6, nEdgeDown = 7, nLoop ; + Point3d ptDown( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + Point3d ptUp = m_mTree[nId].GetTopRight() ; + for ( int k = 0 ; k < nPass ; ++ k) { + if ( m_mTree[nId].m_vInters[k].nIn != -1) { + // trovo il loop che ha l'ingresso o l'uscita più a destra + if ( CheckIfBefore( m_mTree[nId].m_vInters[k].nIn, m_mTree[nId].m_vInters[k].vpt[0], nEdgeUp, ptUp) || + CheckIfBefore( m_mTree[nId].m_vInters[k].nOut, m_mTree[nId].m_vInters[k].vpt.back(), nEdgeUp, ptUp) || + CheckIfBefore( nEdgeDown, ptDown, m_mTree[nId].m_vInters[k].nIn, m_mTree[nId].m_vInters[k].vpt[0]) || + CheckIfBefore( nEdgeDown, ptDown, m_mTree[nId].m_vInters[k].nOut, m_mTree[nId].m_vInters[k].vpt.back())) { + nLoop = k ; + if ( CheckIfBefore( m_mTree[nId].m_vInters[k])) { + ptUp = m_mTree[nId].m_vInters[k].vpt[0] ; + nEdgeUp = m_mTree[nId].m_vInters[k].nIn ; + ptDown = m_mTree[nId].m_vInters[k].vpt.back() ; + nEdgeDown = m_mTree[nId].m_vInters[k].nOut ; + } + else { + ptUp = m_mTree[nId].m_vInters[k].vpt.back() ; + nEdgeUp = m_mTree[nId].m_vInters[k].nOut ; + ptDown = m_mTree[nId].m_vInters[k].vpt[0] ; + nEdgeDown = m_mTree[nId].m_vInters[k].nIn ; + } + bFound = true ; + } + } + else + continue ; + } + if ( bFound && CheckIfBefore( m_mTree[nId].m_vInters[nLoop])) { + bRightMost = true ; + } + // se il mio campione attraversa dall'alto al basso allora il lato destro è dentro!!! + if ( bFound && bRightMost) + m_mTree[nId].m_nRightEdgeIn = 1 ; + else + m_mTree[nId].m_nRightEdgeIn = 0 ; + } + + if ( m_mTree[nId].m_nRightEdgeIn != -1) + return true ; + else + return false ; +} + +//---------------------------------------------------------------------------- +bool +Tree::CategorizeCell( const int& nId) +{ + if ( m_mTree[nId].m_nFlag != -1) { + return true ; + } + INTVECTOR vNeigh, vNeigh1 ; + GetLeftNeigh( nId, vNeigh) ; + // mi servono i vicini di sinistra per capire se sono dentro il loop o fuori + // nRightEdgeIn // 0 right edge fuori, 1 right edge dentro, 2 metà e metà + // nFlag // 0 fuori, 1 intersecata, 2 contiene loop, 3 = 1 & 2, 4 dentro + if ( (int)vNeigh.size() == 0) { + m_mTree[nId].m_nFlag = 0 ; + } + else if ( (int)vNeigh.size() == 1) { + if ( m_mTree[vNeigh[0]].m_nRightEdgeIn == 1) + m_mTree[nId].m_nFlag = 4 ; + else if ( m_mTree[vNeigh[0]].m_nRightEdgeIn == 0) + // devo verificare se la cella è intersecata + if ( m_mTree[vNeigh[0]].m_nFlag == 1 || m_mTree[vNeigh[0]].m_nFlag == 3) + m_mTree[nId].m_nFlag = 0 ; + else { + if ( m_mTree[vNeigh[0]].m_nFlag == 4) { + m_mTree[nId].m_nFlag = 4 ; + } + else if ( m_mTree[vNeigh[0]].m_nFlag == 0) { + m_mTree[nId].m_nFlag = 0 ; + } + } + // se solo parte del right edge del vicino è compreso, allora devo verificare se la cella è contenuta o no + // guardando nFlag del vicino bottom, che è già categorizzato! + else if ( m_mTree[vNeigh[0]].m_nRightEdgeIn == 2) { + GetBottomNeigh( nId, vNeigh1) ; + if ( (int) vNeigh1.size() > 0) { + int nNeigh = vNeigh1[0] ; + if ( m_mTree[nNeigh].m_nFlag == 0 || m_mTree[nNeigh].m_nFlag == 2) { + m_mTree[nId].m_nFlag = 0 ; + } + else if ( m_mTree[nNeigh].m_nFlag == 4) { + m_mTree[nId].m_nFlag = 4 ; + } + else if ( m_mTree[nNeigh].m_nFlag == 1 || m_mTree[nNeigh].m_nFlag == 3) { + if ( m_mTree[nNeigh].m_nRightEdgeIn == 0) + m_mTree[nId].m_nFlag = 0 ; + else if ( m_mTree[nNeigh].m_nRightEdgeIn == 1) + m_mTree[nId].m_nFlag = 4 ; + else if ( m_mTree[nNeigh].m_nRightEdgeIn == 2) { + // in questo caso devo verificare le intersezioni sul lato destro del vicino bottom + // scorro e cerco l'intersezione più alta, se è out la cella è dentro, se è in devo verificare l'out se è più a sinistra o a destra. + int nPass = (int) m_mTree[nNeigh].m_vInters.size() ; + bool bTopMost = false ; + bool bFound = false ; + Point3d ptInters, ptBr( m_mTree[nNeigh].GetTopRight().x, m_mTree[nNeigh].GetBottomLeft().y) ; + ptInters = ptBr ; // inizializzato a vertice bottom right + int nLoop ; + for ( int r = 0 ; r < nPass ; ++ r) { + // trovo il loop che ha l'ingresso o l'uscita più in alto sul lato destro + // verifico che o l'ingresso o l'uscita siano sul lato destro e che sia l'intersezione più alta su quel lato + if ( m_mTree[nNeigh].m_vInters[r].nIn == 3 && ! CheckIfBefore( m_mTree[nNeigh].m_vInters[r].nIn, m_mTree[nNeigh].m_vInters[r].vpt[0], ptInters)) { + nLoop = r ; + ptInters = m_mTree[nNeigh].m_vInters[r].vpt[0] ; + bFound = true ; + } + if ( m_mTree[nNeigh].m_vInters[r].nOut == 3 && ! CheckIfBefore( m_mTree[nNeigh].m_vInters[r].nOut, m_mTree[nNeigh].m_vInters[r].vpt.back(), ptInters)) { + nLoop = r ; + ptInters = m_mTree[nNeigh].m_vInters[r].vpt.back() ; + bFound = true ; + } + // controllo nei vertici + if ( m_mTree[nNeigh].m_vInters[r].nOut == 7 || m_mTree[nNeigh].m_vInters[r].nIn == 7) { + nLoop = r ; + ptInters = m_mTree[nNeigh].GetTopRight() ; + bFound = true ; + break ; + } + if ( m_mTree[nNeigh].m_vInters[r].nOut == 6 || m_mTree[nNeigh].m_vInters[r].nIn == 6) { + nLoop = r ; + ptInters = ptBr ; + bFound = true ; + } + } + if ( bFound && CheckIfBefore( m_mTree[nNeigh].m_vInters[nLoop])) { + bTopMost = true ; + } + if ( bFound && bTopMost) { + if ( AreSameEdge( m_mTree[nNeigh].m_vInters[nLoop].nIn, 3) && AreSameEdge( m_mTree[nNeigh].m_vInters[nLoop].nOut, 3)) { + if ( CheckIfBefore( m_mTree[nNeigh].m_vInters[nLoop])) + m_mTree[nId].m_nFlag = 4 ; + else + m_mTree[nId].m_nFlag = 0 ; + } + else if ( AreSameEdge( m_mTree[nNeigh].m_vInters[nLoop].nOut, 3)) + m_mTree[nId].m_nFlag = 4 ; + else if ( AreSameEdge( m_mTree[nNeigh].m_vInters[nLoop].nIn, 3)) { + // devo verificare se l'uscita è più a sinistra o più a destra della cella + Point3d ptOut = m_mTree[nNeigh].m_vInters[nLoop].vpt.back() ; + Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; + if ( ptOut.x >= ptBr.x) + m_mTree[nId].m_nFlag = 4 ; + else + m_mTree[nId].m_nFlag = 0 ; + } + } + } + } + } + // se non ho vicini bottom devo per forza guardare il vicino a sinistra + else { + Point3d ptInters = m_mTree[vNeigh[0]].GetTopRight() ; + int nEdge = 3 ; + bool bIntersIn ; + for ( int k = 0 ; k < (int) m_mTree[vNeigh[0]].m_vInters.size() ; ++ k) { + if ( AreSameEdge( m_mTree[vNeigh[0]].m_vInters[k].nIn, 3)) { + if ( CheckIfBefore( nEdge, m_mTree[vNeigh[0]].m_vInters[k].vpt[0], ptInters, 3)) { + ptInters = m_mTree[vNeigh[0]].m_vInters[k].vpt[0] ; + bIntersIn = true ; + } + } + if ( AreSameEdge( m_mTree[vNeigh[0]].m_vInters[k].nOut, 3)) { + if ( CheckIfBefore( nEdge, m_mTree[vNeigh[0]].m_vInters[k].vpt.back(), ptInters, 3)) { + ptInters = m_mTree[vNeigh[0]].m_vInters[k].vpt.back() ; + bIntersIn = false ; + } + } + } + if ( bIntersIn) + m_mTree[nId].m_nFlag = 4 ; + else + m_mTree[nId].m_nFlag = 0 ; + } + } + } + else { + for ( int r = 0 ; r < (int)vNeigh.size() ; ++ r) { + if ( m_mTree[vNeigh[r]].m_nRightEdgeIn == 1) { + m_mTree[nId].m_nFlag = 4 ; + break ; + } + else if ( m_mTree[vNeigh[r]].m_nRightEdgeIn == 0) { + if ( m_mTree[vNeigh[r]].m_nFlag == 4) { + m_mTree[nId].m_nFlag = 4 ; + break ; + } + else if ( m_mTree[vNeigh[r]].m_nFlag == 0) { + m_mTree[nId].m_nFlag = 0 ; + break ; + } + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::CheckIfBetween( const Inters& inA, const Inters& inB) const +{ + // controllo se inB è compreso tra l'end e lo start di inA + // ( dall'end di A percorro i bordi della cella fino a tornare allo start e devo incontrare In e Out di B) + INTVECTOR vEdges ; + int nEdge = inA.nOut ; + if ( nEdge > 3 && nEdge != 7) { + nEdge = nEdge - 4 ; + } + else if ( nEdge == 7) + nEdge = 0 ; + // creo la sequenza di Edges da scorrere per trovare i possibili validNextStart + while ( ! AreSameEdge( nEdge, inA.nIn) || (int) vEdges.size() == 0) { + vEdges.push_back( nEdge) ; + if ( nEdge == 3) + nEdge = 0 ; + else + ++ nEdge ; + } + if ( ! AreSameEdge( inA.nIn, inA.nOut)) + vEdges.push_back( nEdge) ; + bool bFound = false ; + for ( int i : vEdges) { + if ( AreSameEdge( inB.nIn, i)) { + if ( AreSameEdge( inB.nIn, inA.nIn) && AreSameEdge( inA.nIn, inA.nOut) && AreSameEdge( inB.nIn, inA.nOut)) { + nEdge = inA.nIn ; + //se l'inizio di A è prima della fine, allora devo controllare che B sia compreso tra Out e In ( esterno) + if ( CheckIfBefore( nEdge, inA.vpt[0], inA.vpt.back(), inA.nOut)) { + if ( CheckIfBefore( nEdge, inA.vpt.back(), inB.vpt[0], inA.nOut ) || CheckIfBefore( nEdge, inB.vpt[0], inA.vpt[0], inA.nOut)) + bFound = true ; + } + // se l'inizio di A è dopo la fine, allora devo controllare che B sia compreso tra In e Out ( interno) + else { + if ( CheckIfBefore( nEdge, inA.vpt.back(), inB.vpt[0], inA.nOut) && CheckIfBefore( nEdge, inB.vpt[0], inA.vpt[0], inA.nOut)) + bFound = true ; + } + } + else if ( AreSameEdge( inB.nIn, inA.nOut)) { + PolyLine pl ; + pl.AddUPoint( 0, inA.vpt[0]) ; + pl.AddUPoint( 1, inA.vpt.back()) ; + Point3d ptEnd ; pl.GetLastPoint( ptEnd) ; + if ( CheckIfBefore( i, ptEnd, inB.vpt[0])) + bFound = true ; + } + else if ( AreSameEdge( inB.nIn, inA.nIn)) { + //devo controllare il loop b sia prima dell'inizio di A + if ( CheckIfBefore( inA.nIn, inB.vpt[0], inA.vpt[0], inB.nIn)) + bFound = true ; + } + else + // devo controllare che inB sia prima di OutB + if ( AreSameEdge( inB.nOut, inB.nIn) && CheckIfBefore( inB.nOut, inB.vpt[0], inB.vpt.back(), inB.nIn)) { + bFound = true ; + } + else if ( ! AreSameEdge( inB.nOut,inB.nIn)) + bFound = true ; + } + // se incontro prima l'uscita dell'ingresso, sicuramente InB NON è un validNextStart + if ( AreSameEdge( inB.nOut, i) && ! bFound && CheckIfBefore( i, inA.vpt[0], inB.vpt.back()) && CheckIfBefore( i, inA.vpt.back(), inB.vpt.back())) + break ; + } + return bFound ; +} + +//---------------------------------------------------------------------------- +bool +Tree::GetLeaves( std::vector& vLeaves) const +{ + vLeaves.clear() ; + for ( int k : m_vnLeaves) { + Cell cToAdd = m_mTree.at( k) ; + vLeaves.push_back( cToAdd) ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +Tree::OnWhichEdge( const int& nId, const Point3d& ptToAssign, int& nEdge) const +{ + Point3d ptTR = m_mTree.at( nId).GetTopRight() ; + Point3d ptBL = m_mTree.at( nId).GetBottomLeft() ; + Point3d ptTl ( ptBL.x, ptTR.y) ; + Point3d ptBr ( ptTR.x, ptBL.y) ; + + if ( ptToAssign.x > ptBL.x && ptToAssign.x < ptTR.x && abs( ptToAssign.y - ptTR.y) < EPS_SMALL) + nEdge = 0 ; + else if ( ptToAssign.y > ptBL.y && ptToAssign.y < ptTR.y && abs( ptToAssign.x - ptBL.x) < EPS_SMALL) + nEdge = 1 ; + else if ( ptToAssign.x > ptBL.x && ptToAssign.x < ptTR.x && abs( ptToAssign.y - ptBL.y) < EPS_SMALL) + nEdge = 2 ; + else if ( ptToAssign.y > ptBL.y && ptToAssign.y < ptTR.y && abs( ptToAssign.x - ptTR.x) < EPS_SMALL) + nEdge = 3 ; + if ( AreSamePointApprox( ptToAssign, ptTR)) + nEdge = 7 ; + else if ( AreSamePointApprox( ptToAssign, ptTl)) + nEdge = 4 ; + else if ( AreSamePointApprox( ptToAssign, ptBL)) + nEdge = 5 ; + else if ( AreSamePointApprox( ptToAssign, ptBr)) + nEdge = 6 ; + else + return false ; + return true ; +} diff --git a/Tree.h b/Tree.h new file mode 100644 index 0000000..3b194ab --- /dev/null +++ b/Tree.h @@ -0,0 +1,204 @@ +//---------------------------------------------------------------------------- +// EgalTech 2023 +//---------------------------------------------------------------------------- +// File : Tree.h Data : 21.04.23 Versione : +// Contenuto : Implementazione della classe Cell di un albero binario Tree. +// +// +// +// Modifiche : 21.04.23 DB Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#pragma once + +//--------------------------- Include ---------------------------------------- +#include +#include "SurfBezier.h" +#include "GeoConst.h" +#include "CurveLine.h" +#include "/EgtDev/Include/EGkPolyLine.h" + +struct Inters { + int nIn ; + PNTVECTOR vpt ; + int nOut ; + bool bCCW ; + bool bVertex ; + int nChunk ; + // riordino le intersezioni per lato in senso antiorario dal top + // se ho più intersezioni che entrano in un lato le riordino considerando che percorro i lati in senso antiorario a partire da ptTR + bool operator < ( Inters& b) { + // trovo in che ordine stanno i due strat, tenendo conto anche della possibilità che siano vertici + INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6, 3} ; + INTVECTOR::iterator iter1 = find( vEdges.begin(), vEdges.end(), nIn) ; + int nPos1 = std::distance( vEdges.begin(), iter1) ; + INTVECTOR::iterator iter2 = find( vEdges.begin(), vEdges.end(), b.nIn) ; + int nPos2 = std::distance( vEdges.begin(), iter2) ; + // se sono loop interni li ordino in modo decrescente rispetto all'area + bool bEqIn = ( nIn == b.nIn) ; + double dAreaA = 0 , dAreaB = 0 ; + if ( bEqIn && nIn == -1) { + PolyLine pl ; + for ( int k = 0 ; k < (int)vpt.size(); ++ k) + pl.AddUPoint( k, vpt[k]) ; + pl.Close() ; + pl.GetAreaXY( dAreaA) ; + pl.Clear() ; + for ( int k = 0 ; k < (int)b.vpt.size(); ++ k) + pl.AddUPoint( k, b.vpt[k]) ; + pl.Close() ; + pl.GetAreaXY( dAreaB) ; + } + // se nIn è un vertice sistemo il valore + int nEdgeIn = nIn ; + if ( nIn > 3) + nEdgeIn = nIn - 4 ; + return nPos1 < nPos2 || + ( bEqIn && nEdgeIn == -1 && abs(dAreaA) > abs(dAreaB)) || + ( bEqIn && nEdgeIn == 0 && vpt[0].x > b.vpt[0].x) || + ( bEqIn && nEdgeIn == 1 && vpt[0].y > b.vpt[0].y) || + ( bEqIn && nEdgeIn == 2 && vpt[0].x < b.vpt[0].x) || + ( bEqIn && nEdgeIn == 3 && vpt[0].y < b.vpt[0].y) + ; } + bool operator == ( Inters& b) { + return AreSamePointExact( vpt[0], b.vpt[0]) ; + } + bool operator != ( Inters& b){ + return ! AreSamePointExact( vpt[0], b.vpt[0]) ; + } +} ; +// nIn e nOut sono flag che indicano da quale lato ho l'ingresso e l'uscita a partire dal lato top in senso antiorario +// oltre il 3 sono le celle adiacenti in diagonale al vertice-> 4 corrisponde al ptTl e da lì in senso antiorario +// -1 se la curva è sempre dentro la cella + +//---------------------------------------------------------------------------- +class Cell +{ + // Edge 0 ( Top) + // Edge 4 ( NW) __________________ Edge 7 ( NE) + // | | + // | | + // Edge 1 ( Left) | | Edge 3 ( Right) + // | | + // | | + // |_________________| + // Edge 5 ( SW) Edge 2 (Bottom) Edge 6 ( SE) + public : + ~Cell( void) ; + Cell( void) ; + Cell( const Point3d& ptBL, const Point3d& ptTR) ; + inline bool IsSame( const Cell& cOtherCell) const ; + void SetBottomLeft( const Point3d ptBL) { m_ptPbl = ptBL ; } + void SetTopRight( const Point3d ptTR) { m_ptPtr = ptTR ; } + void SetSplitDirVert( const bool bVert) { m_bSplitVert = bVert ; } + void SetParent( const int& nParent) { m_nParent = nParent ; } + Point3d GetBottomLeft( void) const { return m_ptPbl ; } + Point3d GetTopRight( void) const { return m_ptPtr ; } + double GetSplitValue( void) const { return m_dSplit ; } + bool IsSplitVert( void) const { return m_bSplitVert ; } // se true la cella verrebbe splittata verticalmente, sennò orizzontalmente + bool IsLeaf( void) const ; // flag che indica se la cella ha figli o se è una foglia + bool IsProcessed( void) const { return m_bProcessed ; } // flag che indica se tutti i figli della cella, se ce ne sono, sono stati processati + void SetProcessed( const bool bProcessed = true) { m_bProcessed = bProcessed ; } + static bool minorX ( const Cell& c1, const Cell& c2) { return c1.m_ptPbl.x < c2.m_ptPbl.x ; } + static bool minorY ( const Cell& c1, const Cell& c2) { return c1.m_ptPbl.y < c2.m_ptPbl.y ; } + + public : + int m_nId ; // Id della cella + int m_nTop ; // cella adiacente al lato top + int m_nBottom ; // cella adiacente al lato bottom + int m_nLeft ; // cella adiacente al lato left + int m_nRight ; // cella adiacente al lato right + int m_nParent ; // cella genitore + int m_nDepth ; // profondità della cella rispetto a root + double m_dSplit ; // parametro a cui è stata splittata la cella + int m_nChild1 ; // prima cella figlio + int m_nChild2 ; // seconda cella figlio + int m_nFlag ; // falg che indica la caratterizzazione della cella rispetto ai loop di trim + // 0 esterna, 1 intersecata, 2 contiene un loop, 3 intersecata e contenente un loop, 4 contenuta in un loop + int m_nFlag2 ; // falg che indica se la cella è stata attraversata durante l'ultima fase del labelling + int m_nRightEdgeIn ; // 0 right edge fuori, 1 right edge dentro, 2 metà e metà + bool m_bOnLeftEdge ; // flag che indica se la cella è sul lato sinistro ( per superfici chiuse sul parametro U) + bool m_bOnTopEdge ; // flag che indica se la cella è sul lato top ( per superfici chiuse sul parametro V) + std::vector m_vInters ; // vettore delle intersezioni della cella con i loop di trim + // ogni elemento del vettore è l'insieme dei punti che caratterizza un atrtaversamento della cella + + private : + Point3d m_ptPbl ; // punto bottom left + Point3d m_ptPtr ; // punto top right + bool m_bProcessed ; // flag che indica se la cella è stata processata + bool m_bSplitVert ; // flag che indica in quale direzione è stata divisa la cella +} ; + +//---------------------------------------------------------------------------- +class Tree +{ +public : + ~Tree( void) ; + Tree( void) ; + Tree ( const SurfBezier* pSrfBz, const bool bSplitPatches = true, const Point3d ptMin = ORIG, const Point3d ptMax = ORIG) ; + void SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches = true, const Point3d ptMin = ORIG, const Point3d ptMax = ORIG) ; + bool GetIndependentTrees( std::vector>& vTrees) ; // calcolo la suddivisione della superficie solo sulle singole bbox dei loop di trim ( unendo quelli vicini) + bool BuildTree( const double& dLinTol = LIN_TOL_STD, const double& dSideMin = 1, const double& dSideMax = INFINITO) ; // dSideMax è il massimo per la dimensione maggiore di un triangolo della trimesh + // dSideMin è lunghezza minima del lato di una cella nello spazio reale + bool BuildTree_test( const double& dLinTol = LIN_TOL_STD, const double& dSideMin = 1, const double& dSideMax = INFINITO) ; + bool GetPolygons( std::vector& vPolygons) ; + bool GetPolygonsBasic( POLYLINEVECTOR& vPolygons) ; // restituisce il poligono corrispondente ad ogni cella foglia dell'albero + // ad ogni poligono sono stati aggiunti tutti i vertici dei vicini posizionati sui suoi lati + bool GetLeaves ( std::vector& vLeaves) const ; + +private : + void Split( const int& nId, const double& dSplitValue) ; // funzione di split di una cella al parametro indicato nella direzione data da bVert + void Split( const int& nId) ; // funzione di split di una cella dell'albero a metà nella direzione data da bVert + void Balance () ; // creo rami in modo che tutte tutte le foglie abbiano come adiacenti foglie ad una profonditù di +- 1 + int GetHeightLeaves ( const int& nId, INTVECTOR& vnLeaves, int d = 0) const ; // altezza del subtree a partire dal nodo nId + int GetDepth ( const int& nId, const int& nRef) const ; // livello del nodo nId + void GetTopNeigh( const int& nId, INTVECTOR& vTopNeighs) const ; // restituisce le celle foglie che sono adiacenti al lato top + void GetBottomNeigh( const int& nId, INTVECTOR& vBottomNeighs) const ; // restituisce le celle foglie che sono adiacenti al lato bottom + void GetLeftNeigh( const int& nId, INTVECTOR& vLeftNeighs) const ; // restituisce le celle foglie che sono adiacenti al lato left + void GetRightNeigh( const int& nId, INTVECTOR& vRightNeighs) const ; // restituisce le celle foglie che sono adiacenti al lato right + void GetRootNeigh( const int& nEdge, INTVECTOR& vNeigh) ; // restituisce le foglie dell'albero che sono adiacenti al lato nEdge, numerato a partire dal top ( 0) in senso antiorario + void ResetTree ( void) ; // resetto m_bProcessed a false per tutti i nodi dell'albero + INTVECTOR FindCell ( const Point3d& ptToAssign, const CurveLine& cl) const ; // dato un punto, trova la cella foglia a cui appartiene + INTVECTOR FindCell ( const Point3d& ptToAssign, const CurveLine& cl, INTVECTOR vCells) const ; // dato un punto, trova la cella foglia a cui appartiene + bool TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) ; // tracing dei loop e labelling delle celle + bool FindInters( int& nId, const CurveLine& clTrim, PNTVECTOR& vptInters, bool bFirstInters = true) ; // trova le intersezioni tra una cella e una linea di trim + // resituisce l'id della cella verso cui la curva di trim esce e il vettore delle intersezioni per la cella successiva con il primo punto + bool CreateCellPolygons ( const int& nLeafId, std::vector& vPolygons, INTVECTOR& vToCheck, int& nPoly, INTVECTOR& vnParentChunk, const PolyLine& plCell) ; + bool CreateIslandAndHoles ( const int& nLeafId, std::vector& vPolygons, int& nPoly, INTVECTOR& vnParentChunk) ; + bool CheckIfBefore( const PolyLine& pl, const int& nEdge) const ; + bool CheckIfBefore( const Inters& inA) const ; + bool CheckIfBefore( const int& nEdge1, const Point3d& ptP1, const int& nEdge2, const Point3d& ptP2) const ; // punto 1 su edge 1 e punto 2 su edge 2, rispetto al lato 3 + bool CheckIfBefore( const int& nEdge, const Point3d& ptP1, const Point3d& ptP2, const int& nEdge2 = -1) const ; // entrambi i punti sullo stesso lato, nEdge. nEdge2 serve come backup, in caso nEdge sia un vertice. + bool AreSameEdge( const int& nEdge1, const int nEdge2) const ; + bool AddVertex( const int& nId, const std::vector& vEdgeVertex, PolyLine& plTrimmedPoly, int& c, const Point3d& ptToAdd) const ; + //bool SetRightEdgeIn( int nId, std::vector& vEdgeVertex, PolyLine& plTrimmedPoly) ; + bool SetRightEdgeIn( const int& nId) ; + bool CategorizeCell( const int& nId) ; + bool CheckIfBetween( const Inters& inA, const Inters& inB) const ; + bool OnWhichEdge( const int& nId, const Point3d& ptToAssign, int& nEdge) const ; + +private : + const SurfBezier* m_pSrfBz ; // superficie di bezier + DBLVECTOR m_vDim ; // distanze tra i vertici della superficie di bezier in 3d in ordine antiorario a partire da ptP00 + bool m_bTrimmed ; // superficie trimmata + std::vector m_vChunk ; // elenco dei loop divisi per chunk + std::map m_mChunk ; + ICURVEPOVECTOR m_vLoop ; // curve di loop + std::vector> m_vPlApprox ; + bool m_bBilinear ; // superficie bilineare + bool m_bMulti ; // superficie multi-patch + bool m_bClosedU ; // superficie chiusa lungo il parametro U + bool m_bClosedV ; // superficie chiusa lungo il parametro V + bool m_bSplitPatches ; // flag che indica se le patches sono state divise prima della creazione dell'albero + int m_nDegU ; // grado della superficie nel parametro U + int m_nDegV ; // grado della superficie nel parametro V + int m_nSpanU ; + int m_nSpanV ; + std::vector m_vPolygons ; // vettore dei poligoni del tree + std::map m_mTree ; // mappa che contiene tutti i nodi e le foglie dell'albero. -2 è puntatore Null e -1 è root + std::map m_mVert ; // mappa che contiene tutti i vertici 3d delle celle del tree. L'Id è lo stesso che la cella ha in m_mTree + INTVECTOR m_vnLeaves ; // vettore delle foglie + INTVECTOR m_vnParents ; // vettore delle celle ottenute dalla divisione preliminare in singole patch +} ; \ No newline at end of file