//---------------------------------------------------------------------------- // 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 "/EgtDev/Include/EGkPolyLine.h" #include "/EgtDev/Include/EGkDistPointCurve.h" using namespace std ; //---------------------------------------------------------------------------- Cell::Cell( void) : m_nId( -1), m_ptPbl( ORIG), m_bProcessed ( false) , m_bSplitVert ( true) , m_nTop ( -2), m_nBottom( -2), m_nLeft( -2), m_nRight ( -2), m_nParent( -2), m_nDepth( 0), m_nChild1( -2), m_nChild2( -2) { Point3d ptTr ( 1, 1) ; m_ptPtr = ptTr ; } //---------------------------------------------------------------------------- Cell::Cell( Point3d ptBL, Point3d ptTR) : m_nId( -1), m_ptPbl( ptBL), m_ptPtr(ptTR), m_bProcessed ( false) , m_bSplitVert ( true) , m_nTop ( -2), m_nBottom( -2), m_nLeft( -2), m_nRight ( -2), m_nParent( -2), m_nDepth( 0), m_nChild1( -2), m_nChild2( -2) {} //---------------------------------------------------------------------------- Cell::~Cell( void) { } //---------------------------------------------------------------------------- inline bool Cell::IsSame( Cell cOtherCell) { if ( AreSamePointXYApprox( m_ptPbl, cOtherCell.GetBottomLeft()) && AreSamePointXYApprox( m_ptPtr, cOtherCell.GetTopRight())) { return true ; } else { return false ; } } //---------------------------------------------------------------------------- bool //Cell::IsLeaf ( void) const Cell::IsLeaf ( void) { if( m_nChild1 == -2 && m_nChild2 == -2) return true ; else return false ; } //---------------------------------------------------------------------------- Tree::Tree( void) : m_dLinTol(LIN_TOL_FINE), m_pSrfBz(nullptr), m_nRoot( -1), m_bTrimmed( false) { Point3d ptBl( 0, 0), ptTr ( 1, 1) ; Cell cRoot( ptBl, ptTr) ; m_mTree.insert( pair< int, Cell>( m_nRoot, cRoot)) ; } //---------------------------------------------------------------------------- Tree::Tree( const SurfBezier* pSrfBz) : m_dLinTol( LIN_TOL_FINE), m_pSrfBz ( pSrfBz), m_nRoot( -1) { // 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 ; Point3d ptTop( nSpanU, nSpanV) ; Cell cRoot( ORIG, ptTop) ; m_mTree.insert( pair< int, Cell>( m_nRoot, cRoot)) ; } //---------------------------------------------------------------------------- Tree::~Tree( void) { } //---------------------------------------------------------------------------- void Tree::SetSurf( const SurfBezier* pSrfBz) { m_pSrfBz = pSrfBz ; // 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 ; Point3d ptTop( nSpanU, nSpanV) ; Cell cRoot( ORIG, ptTop) ; m_mTree.insert( pair< int, Cell>( -1, cRoot)) ; } //---------------------------------------------------------------------------- void Tree::Split( int nId) { 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( pair( nNodes - 1, cChild1)) ; m_mTree.insert( pair( nNodes, cChild2)) ; if ( ! m_mTree[nId].IsSplitVert()) { // la cella figlio 1 è quella sopra Point3d ptBL( m_mTree[nId].GetBottomLeft().x, ( m_mTree[nId].GetBottomLeft().y + m_mTree[nId].GetTopRight().y) / 2) ; 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, ( m_mTree[nId].GetTopRight().y + m_mTree[nId].GetBottomLeft().y) / 2) ; 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 ; } else { // la cella figlio 1 è quella di sinistra Point3d ptTR( ( m_mTree[nId].GetBottomLeft().x + m_mTree[nId].GetTopRight().x) / 2, 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(( m_mTree[nId].GetBottomLeft().x + m_mTree[nId].GetTopRight().x) / 2, 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 ; } m_mTree[m_mTree[nId].m_nChild1].SetParent( nId) ; m_mTree[m_mTree[nId].m_nChild2].SetParent( nId) ; //m_bProcessed = true ; } ////---------------------------------------------------------------------------- //bool Tree::BuildTree(void) //{ // // di default SplitVert è true ! // int ToSplit = -1; // m_mTree[ToSplit].SetSplitDirVert( true) ; // Split( ToSplit) ; // // celle 2 e 3 // ToSplit = m_mTree[-1].m_nChild1 ; // m_mTree[ToSplit].SetSplitDirVert( false) ; // Split( ToSplit) ; // // celle 4 e 5 // ToSplit = m_mTree[ToSplit].m_nChild1 ; // m_mTree[ToSplit].SetSplitDirVert( true) ; // Split( ToSplit) ; // // celle 6 e 7 // ToSplit = m_mTree[ToSplit].m_nChild2 ; // m_mTree[ToSplit].SetSplitDirVert( false) ; // Split( ToSplit) ; // // celle 8 e 9 // ToSplit = m_mTree[ToSplit].m_nChild2 ; // m_mTree[ToSplit].SetSplitDirVert( true) ; // Split( ToSplit) ; // // celle 10 e 11 // ToSplit = m_mTree[ToSplit].m_nChild1 ; // m_mTree[ToSplit].SetSplitDirVert( false) ; // Split( ToSplit) ; // // celle 12 e 13 // ToSplit = m_mTree[ToSplit].m_nParent ; // ToSplit = m_mTree[ToSplit].m_nChild2 ; // m_mTree[ToSplit].SetSplitDirVert( true) ; // Split( ToSplit) ; // INTVECTOR vTops, vBottoms, vLefts, vRights , vLeaves1, vLeaves2, vLeaves; // int d1, d2 , H1 ; // GetTopNeigh( 3, vTops) ; // GetBottomNeigh( 6, vBottoms) ; // GetLeftNeigh( 1, vLefts) ; // GetRightNeigh( 4, vRights ) ; // d1 = GetHeightLeaves( 7, vLeaves1) ; // d2 = GetHeightLeaves( 5, vLeaves2) ; // H1 = GetHeightLeaves( -1, vLeaves) ; // m_vnLeaves = vLeaves ; // int i = 0 ; // // return true ; //} ////---------------------------------------------------------------------------- //bool Tree::BuildTree( void) //{ // int ToSplit = -1 ; // m_mTree[ToSplit].SetSplitDirVert( true) ; // Split( ToSplit) ; // // celle 2 e 3 // ToSplit = m_mTree[-1].m_nChild1 ; // m_mTree[ToSplit].SetSplitDirVert( false) ; // Split( ToSplit) ; // // return true ; //} //---------------------------------------------------------------------------- bool Tree::BuildTree( double dLinTol, double dSideMax) { // trovo dove splittare la cella e creo i puntatori ai figli // comincio a suddividere la superficie usando un kd-tree // approssimo con una bilineare e se l'errore di approssimazione è troppo grande cerco una direzione // in cui dividere la superficie double dErr ; // errore calcolato double dDist; // distanza tra punti selezionati sulla isoparametrica double dSideMinVal, dSideMaxVal ; // lunghezza del lato della eventuale cella figlio double dSplit = 0.5 ; // effettuo sempre split a metà double dSideMin = 0.05 ; // lunghezza minima del lato di una cella //double dSidemin = 0.1 ; // lunghezza minima del lato di una cella //m_dLinTol = dLinTol ; m_dLinTol = 0.2 ; int nSteps = 51 ; // numero di Step mentre scorro lungo un'isoparametrica double dU, dV , dULoc, dVLoc ; //double dIter, dIterLoc ; bool bVert ; Point3d ptBz, ptBl ; // cerco lo scostamento massimo tra la sup di Bezier e la sua approssimazione bilineare INTVECTOR vBalanceCheck ; int nCToSplit = -1 ; int c = 1 ; while ( nCToSplit != -2 && m_mTree[nCToSplit].IsProcessed() == false) { dErr = 0 ; // calcolo l'errore di approssimazione //// calcolo l'errore di approssimazione confrontando con la bilineare corrispondente //SurfBezier pSrfBl ; //pSrfBl.Init(1, 1, 1, 1, false) ; //m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x, m_mTree[nCToSplit].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; //pSrfBl.SetControlPoint( 0, ptBz) ; // P00 //m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x, m_mTree[nCToSplit].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; //pSrfBl.SetControlPoint( 1, ptBz) ; // P10 //m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x, m_mTree[nCToSplit].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; //pSrfBl.SetControlPoint( 2, ptBz) ; // P01 //m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x, m_mTree[nCToSplit].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; //pSrfBl.SetControlPoint( 3, ptBz) ; // P11 //// scorro lungo le isoparametriche a questi valori di U e V per trovare dov'è il punto di maggior discostamento //dU = ( m_mTree[nCToSplit].GetTopRight().x + m_mTree[nCToSplit].GetBottomLeft().x) / 2 ; //dV = ( m_mTree[nCToSplit].GetTopRight().y + m_mTree[nCToSplit].GetBottomLeft().y) / 2 ; //dULoc = ( dU - m_mTree[nCToSplit].GetBottomLeft().x) / ( m_mTree[nCToSplit].GetTopRight().x - m_mTree[nCToSplit].GetBottomLeft().x) ; //dVLoc = ( dV - m_mTree[nCToSplit].GetBottomLeft().y) / ( m_mTree[nCToSplit].GetTopRight().y - m_mTree[nCToSplit].GetBottomLeft().y) ; //double dErrUmax = 0, dErrVmax = 0 ; //for ( int i = 0 ; i < nSteps ; ++ i){ // dIter = double ( i) / double ( nSteps - 1) ; // dIterLoc = ( 1 - dIter) * m_mTree[nCToSplit].GetBottomLeft().y + dIter * m_mTree[nCToSplit].GetTopRight().y ; // if ( ! m_pSrfBz->GetPointD1D2( dU, dIterLoc, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) || // ! pSrfBl.GetPointD1D2( dULoc, dIter, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBl)) // return false ; // dDist = Dist( ptBz, ptBl) ; // dErrVmax = max ( dErrVmax, dDist) ; // if ( dDist > dErr) { // dErr = dDist ; // //bVert = true ; // } // dIterLoc = ( 1 - dIter) * m_mTree[nCToSplit].GetBottomLeft().x + dIter * m_mTree[nCToSplit].GetTopRight().x ; // if ( ! m_pSrfBz->GetPointD1D2( dIterLoc, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) || // ! pSrfBl.GetPointD1D2( dIter, dVLoc, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBl)) // return false ; // dDist = Dist( ptBz, ptBl) ; // dErrUmax = max ( dErrUmax, dDist) ; // if ( dDist > dErr) { // dErr = dDist ; // //bVert = false ; // } //} Point3d ptP00, ptP10, ptP11, ptP01 ; m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x, m_mTree[nCToSplit].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00) ; m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x, m_mTree[nCToSplit].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10) ; m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x, m_mTree[nCToSplit].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01) ; m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x, m_mTree[nCToSplit].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11) ; CurveLine cl0010, cl0001, cl1011, cl0111 ; // U=0 cl0010.Set( ptP00, ptP10) ; //// V=0 //cl0001.Set( ptP00, ptP01) ; // U=1 cl0111.Set( ptP01, ptP11) ; //// V=1 //cl0111.Set( ptP01, ptP11) ; Point3d pt0010, pt0111, ptBz0, ptBz1, ptBzV ; int nFlag ; CurveLine clV ; for ( int u = 0 ; u < nSteps ; ++ u){ dU = double ( u) / double ( nSteps - 1) ; dULoc = ( 1 - dU) * m_mTree[nCToSplit].GetBottomLeft().x + dU * m_mTree[nCToSplit].GetTopRight().x ; if ( ! m_pSrfBz->GetPointD1D2( dULoc, m_mTree[nCToSplit].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz0) || ! m_pSrfBz->GetPointD1D2( dULoc, m_mTree[nCToSplit].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz1)) return false ; DistPointCurve dpc0010( ptBz0, cl0010) ; DistPointCurve dpc0111( ptBz1, cl0111) ; dpc0010.GetMinDistPoint( 0, pt0010, nFlag) ; dpc0111.GetMinDistPoint( 0, pt0111, nFlag) ; clV.Set( pt0010, pt0111) ; for ( int v = 0 ; v < nSteps ; ++ v){ dV = double ( v) / double ( nSteps - 1) ; dVLoc = ( 1 - dV) * m_mTree[nCToSplit].GetBottomLeft().y + dV * m_mTree[nCToSplit].GetTopRight().y ; if ( ! m_pSrfBz->GetPointD1D2( dULoc, dVLoc, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBzV)) return false ; DistPointCurve dpc( ptBzV, clV) ; dpc.GetDist( dDist) ; if ( dDist > dErr) dErr = dDist ; } } // calcolo in quale direzione ho più curvatura // devo trovare i punti sui lati corrispondenti a dUmax e dVmax, unendo queste coppie trovo le due direzioni di possibile split // punti medi del lato successivo in senso antiorario rispetto al relativo vertice della patch dU = ( m_mTree[nCToSplit].GetTopRight().x + m_mTree[nCToSplit].GetBottomLeft().x) / 2 ; dV = ( m_mTree[nCToSplit].GetTopRight().y + m_mTree[nCToSplit].GetBottomLeft().y) / 2 ; dULoc = dVLoc = 0.5 ; Point3d ptPSrf ; //ptP00, ptP10, ptP11, ptP01; m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; m_pSrfBz->GetPointD1D2( dU, m_mTree[nCToSplit].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00) ; m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetTopRight().x, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10) ; m_pSrfBz->GetPointD1D2( dU, m_mTree[nCToSplit].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11) ; m_pSrfBz->GetPointD1D2( m_mTree[nCToSplit].GetBottomLeft().x, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01) ; Point3d ptP00P11 = ( 1 - dULoc) * ptP00 + dULoc * ptP11 ; Point3d ptP10P01 = ( 1 - dVLoc) * ptP10 + dVLoc * ptP01 ; // per lo split scelgo la direzione che è più vicina alla superficie originale nel punto di maggior distanza // effettuo lo split e configuro le celle figlie if ( Dist(ptP00P11, ptPSrf) > Dist(ptP10P01, ptPSrf)) // lungo la direzione U ho una curvatura maggiore bVert = false ; else // lungo la direzione V ho una curvatura maggiore bVert = true ; // verifico che la cella sia da splittare e che eventualmente sia abbastanza grande da poterlo fare if ( bVert) dSideMinVal = min( Dist( ptP00, ptP10), Dist( ptP01, ptP11)) ; else dSideMinVal = min( Dist( ptP00, ptP01), Dist( ptP10, ptP11)) ; dSideMaxVal = max( Dist( ptP00, ptP11), Dist( ptP10, ptP01)) ; if ( ( dErr > m_dLinTol && dSideMinVal >= dSideMin) || 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].Processed() ; // risalgo i parent finché non trovo il primo Child2 da processare 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].Processed() ; 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].Processed() ; if ( nCToSplit == m_nRoot && m_mTree[m_mTree[nCToSplit].m_nChild2].IsProcessed() ) break ; } nCToSplit = m_mTree[nCToSplit].m_nChild2 ; } c ++ ; } Balance( vBalanceCheck) ; return true ; } //---------------------------------------------------------------------------- void Tree::Balance( INTVECTOR vCheck) { for ( int i : vCheck ) { // non ancora implementato // rendolo il tree balanced : ogni foglia deve avere una profondità di +- 1 rispetto ai suoi vicini. } } //---------------------------------------------------------------------------- void Tree::GetTopNeigh( int nId, INTVECTOR& vTopNeighs) { if ( (int) vTopNeighs.size() == 0) { if ( m_mTree[nId].m_nTop == -2) return ; if ( m_mTree[m_mTree[nId].m_nTop].IsLeaf()) vTopNeighs.push_back( m_mTree[nId].m_nTop) ; else { if ( m_mTree[m_mTree[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[m_mTree[nId].m_nTop].GetTopRight().x - m_mTree[m_mTree[nId].m_nTop].GetBottomLeft().x <= m_mTree[nId].GetTopRight().x - m_mTree[nId].GetBottomLeft().x) { vTopNeighs.push_back( m_mTree[m_mTree[nId].m_nTop].m_nChild1) ; vTopNeighs.push_back( m_mTree[m_mTree[nId].m_nTop].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else{ if ( m_mTree[m_mTree[m_mTree[nId].m_nTop].m_nChild1].GetTopRight().x <= m_mTree[nId].GetBottomLeft().x || m_mTree[m_mTree[m_mTree[nId].m_nTop].m_nChild1].GetBottomLeft().x >= m_mTree[nId].GetTopRight().x ) vTopNeighs.push_back( m_mTree[m_mTree[nId].m_nTop].m_nChild2) ; else vTopNeighs.push_back( m_mTree[m_mTree[nId].m_nTop].m_nChild1) ; } } else { vTopNeighs.push_back( m_mTree[m_mTree[nId].m_nTop].m_nChild2) ; } } bool bAllLeaves = true ; for ( int i : vTopNeighs ) { if ( ! m_mTree[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[j] ; if ( m_mTree[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[i].IsSplitVert() ) { // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima if ( m_mTree[i].GetTopRight().x - m_mTree[i].GetBottomLeft().x <= m_mTree[nId].GetTopRight().x - m_mTree[nId].GetBottomLeft().x) { vTopNeighs.push_back( m_mTree[i].m_nChild1) ; vTopNeighs.push_back( m_mTree[i].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else { if ( m_mTree[m_mTree[i].m_nChild1].GetTopRight().x <= m_mTree[nId].GetBottomLeft().x || m_mTree[m_mTree[i].m_nChild1].GetBottomLeft().x >= m_mTree[nId].GetTopRight().x ) vTopNeighs.push_back( m_mTree[i].m_nChild2) ; else vTopNeighs.push_back( m_mTree[i].m_nChild1) ; } } else { vTopNeighs.push_back( m_mTree[i].m_nChild2) ; } } } } vector vCells ; for ( int k : vTopNeighs ) vCells.push_back( m_mTree[k]) ; sort( vCells.begin(), vCells.end(), Cell::minorX ) ; vTopNeighs.clear() ; for ( Cell c : vCells) vTopNeighs.push_back( c.m_nId) ; } //---------------------------------------------------------------------------- void Tree::GetBottomNeigh( int nId, INTVECTOR& vBottomNeighs) { if ( (int) vBottomNeighs.size() == 0) { if ( m_mTree[nId].m_nBottom == -2) return ; if ( m_mTree[m_mTree[nId].m_nBottom].IsLeaf()) vBottomNeighs.push_back( m_mTree[nId].m_nBottom) ; else { if ( m_mTree[m_mTree[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[m_mTree[nId].m_nBottom].GetTopRight().x - m_mTree[m_mTree[nId].m_nBottom].GetBottomLeft().x <= m_mTree[nId].GetTopRight().x - m_mTree[nId].GetBottomLeft().x) { vBottomNeighs.push_back( m_mTree[m_mTree[nId].m_nBottom].m_nChild1) ; vBottomNeighs.push_back( m_mTree[m_mTree[nId].m_nBottom].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else{ if ( m_mTree[m_mTree[m_mTree[nId].m_nBottom].m_nChild1].GetTopRight().x <= m_mTree[nId].GetBottomLeft().x || m_mTree[m_mTree[m_mTree[nId].m_nBottom].m_nChild1].GetBottomLeft().x >= m_mTree[nId].GetTopRight().x ) vBottomNeighs.push_back( m_mTree[m_mTree[nId].m_nBottom].m_nChild2) ; else vBottomNeighs.push_back( m_mTree[m_mTree[nId].m_nBottom].m_nChild1) ; } } else { vBottomNeighs.push_back( m_mTree[m_mTree[nId].m_nBottom].m_nChild1) ; } } bool bAllLeaves = true ; for ( int i : vBottomNeighs ) { if ( ! m_mTree[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[j] ; if ( m_mTree[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[i].IsSplitVert()) { // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima if ( m_mTree[i].GetTopRight().x - m_mTree[i].GetBottomLeft().x <= m_mTree[nId].GetTopRight().x - m_mTree[nId].GetBottomLeft().x) { vBottomNeighs.push_back( m_mTree[i].m_nChild1) ; vBottomNeighs.push_back( m_mTree[i].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else { if ( m_mTree[m_mTree[i].m_nChild1].GetTopRight().x <= m_mTree[nId].GetBottomLeft().x || m_mTree[m_mTree[i].m_nChild1].GetBottomLeft().x >= m_mTree[nId].GetTopRight().x ) vBottomNeighs.push_back( m_mTree[i].m_nChild2) ; else vBottomNeighs.push_back( m_mTree[i].m_nChild1) ; } } else { vBottomNeighs.push_back( m_mTree[i].m_nChild1) ; } } } } vector vCells ; for ( int k : vBottomNeighs ) vCells.push_back( m_mTree[k]) ; sort( vCells.begin(), vCells.end(), Cell::minorX) ; vBottomNeighs.clear() ; for ( Cell c : vCells) vBottomNeighs.push_back( c.m_nId) ; } //---------------------------------------------------------------------------- void Tree::GetLeftNeigh( int nId, INTVECTOR& vLeftNeighs) { if ( (int) vLeftNeighs.size() == 0 ) { if ( m_mTree[nId].m_nLeft == -2) return ; if ( m_mTree[m_mTree[nId].m_nLeft].IsLeaf()) vLeftNeighs.push_back( m_mTree[nId].m_nLeft) ; else { if ( ! m_mTree[m_mTree[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[m_mTree[nId].m_nLeft].GetTopRight().y - m_mTree[m_mTree[nId].m_nLeft].GetBottomLeft().y <= m_mTree[nId].GetTopRight().y - m_mTree[nId].GetBottomLeft().y) { vLeftNeighs.push_back( m_mTree[m_mTree[nId].m_nLeft].m_nChild1) ; vLeftNeighs.push_back( m_mTree[m_mTree[nId].m_nLeft].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else{ if ( m_mTree[m_mTree[m_mTree[nId].m_nLeft].m_nChild1].GetTopRight().y <= m_mTree[nId].GetBottomLeft().y || m_mTree[m_mTree[m_mTree[nId].m_nLeft].m_nChild1].GetBottomLeft().y >= m_mTree[nId].GetTopRight().y ) vLeftNeighs.push_back( m_mTree[m_mTree[nId].m_nLeft].m_nChild2) ; else vLeftNeighs.push_back( m_mTree[m_mTree[nId].m_nLeft].m_nChild1) ; } } else { vLeftNeighs.push_back( m_mTree[m_mTree[nId].m_nLeft].m_nChild2) ; } } bool bAllLeaves = true ; for ( int i : vLeftNeighs ) { if ( ! m_mTree[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[j] ; if ( m_mTree[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[i].IsSplitVert()) { // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima if ( m_mTree[i].GetTopRight().y - m_mTree[i].GetBottomLeft().y <= m_mTree[nId].GetTopRight().y - m_mTree[nId].GetBottomLeft().y) { vLeftNeighs.push_back( m_mTree[i].m_nChild1) ; vLeftNeighs.push_back( m_mTree[i].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else { if ( m_mTree[m_mTree[i].m_nChild1].GetTopRight().y <= m_mTree[nId].GetBottomLeft().y || m_mTree[m_mTree[i].m_nChild1].GetBottomLeft().y >= m_mTree[nId].GetTopRight().y ) vLeftNeighs.push_back( m_mTree[i].m_nChild2) ; else vLeftNeighs.push_back( m_mTree[i].m_nChild1) ; } } else { vLeftNeighs.push_back( m_mTree[i].m_nChild2) ; } } } } vector vCells ; for ( int k : vLeftNeighs) vCells.push_back( m_mTree[k]) ; sort( vCells.begin(), vCells.end(), Cell::minorY) ; vLeftNeighs.clear() ; for ( Cell c : vCells) vLeftNeighs.push_back( c.m_nId) ; } //---------------------------------------------------------------------------- void Tree::GetRightNeigh( int nId, INTVECTOR& vRightNeighs) { if ( (int) vRightNeighs.size() == 0) { if ( m_mTree[nId].m_nRight == -2) return ; if ( m_mTree[m_mTree[nId].m_nRight].IsLeaf()) vRightNeighs.push_back( m_mTree[nId].m_nRight) ; else { if ( ! m_mTree[m_mTree[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[m_mTree[nId].m_nRight].GetTopRight().y - m_mTree[m_mTree[nId].m_nRight].GetBottomLeft().y <= m_mTree[nId].GetTopRight().y - m_mTree[nId].GetBottomLeft().y) { vRightNeighs.push_back( m_mTree[m_mTree[nId].m_nRight].m_nChild1) ; vRightNeighs.push_back( m_mTree[m_mTree[nId].m_nRight].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else{ if ( m_mTree[m_mTree[m_mTree[nId].m_nRight].m_nChild1].GetTopRight().y <= m_mTree[nId].GetBottomLeft().y || m_mTree[m_mTree[m_mTree[nId].m_nRight].m_nChild1].GetBottomLeft().y >= m_mTree[nId].GetTopRight().y ) vRightNeighs.push_back( m_mTree[m_mTree[nId].m_nRight].m_nChild2) ; else vRightNeighs.push_back( m_mTree[m_mTree[nId].m_nRight].m_nChild1) ; } } else { vRightNeighs.push_back( m_mTree[m_mTree[nId].m_nRight].m_nChild1) ; } } bool bAllLeaves = true ; for ( int i : vRightNeighs) { if ( ! m_mTree[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[j] ; if ( m_mTree[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[i].IsSplitVert()) { // se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima if ( m_mTree[i].GetTopRight().y - m_mTree[i].GetBottomLeft().y <= m_mTree[nId].GetTopRight().y - m_mTree[nId].GetBottomLeft().y) { vRightNeighs.push_back( m_mTree[i].m_nChild1) ; vRightNeighs.push_back( m_mTree[i].m_nChild2) ; } // altrimenti solo uno dei figli lo sarà else { if ( m_mTree[m_mTree[i].m_nChild1].GetTopRight().y <= m_mTree[nId].GetBottomLeft().y || m_mTree[m_mTree[i].m_nChild1].GetBottomLeft().y >= m_mTree[nId].GetTopRight().y ) vRightNeighs.push_back( m_mTree[i].m_nChild2) ; else vRightNeighs.push_back( m_mTree[i].m_nChild1) ; } } else { vRightNeighs.push_back( m_mTree[i].m_nChild1) ; } } } } vector vCells ; for ( int k : vRightNeighs) vCells.push_back( m_mTree[k]) ; sort( vCells.begin(), vCells.end(), Cell::minorY) ; vRightNeighs.clear() ; for ( Cell c : vCells) vRightNeighs.push_back( c.m_nId) ; } //---------------------------------------------------------------------------- int Tree::GetHeightLeaves( int nId, INTVECTOR& vnLeaves, int d) { if ( (int) vnLeaves.size() == 0) { if ( m_mTree[nId].IsLeaf()) return d ; else { vnLeaves.push_back( m_mTree[nId].m_nChild1) ; vnLeaves.push_back( m_mTree[nId].m_nChild2) ; if ( ! m_mTree[m_mTree[nId].m_nChild1].IsLeaf() || ! m_mTree[m_mTree[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[m_mTree[nId].m_nChild1].m_nDepth) ; } } else { for ( int j = 0 ; j != (int) vnLeaves.size() ; ++ j) { int i = vnLeaves[j] ; if ( m_mTree[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[i].m_nChild1) ; vnLeaves.push_back( m_mTree[i].m_nChild2) ; d = max ( d, m_mTree[m_mTree[i].m_nChild1].m_nDepth) ; } } return d ; } return d - m_mTree[nId].m_nDepth ; } //---------------------------------------------------------------------------- int Tree::GetDepth( int nId, int nRef = -2) { int c = 0 ; while ( m_mTree[nId].m_nParent != nRef ) { nId = m_mTree[nId].m_nParent ; ++ c ; } return c ; } //---------------------------------------------------------------------------- bool Tree::GetPolygons( POLYLINEVECTOR& vPolygons) { if ( m_vPolygons.empty()) { PNTVECTOR vVertices ; INTVECTOR vNeigh ; bool bBottomRight , bTopLeft ; // scorro lungo tutte le celle leaves ( dell'albero bilanciato) e oltre agli angoli della cella aggiungo alla polyline anche i vertici sui lati for ( int nId : m_vnLeaves) { vVertices.clear() ; vNeigh.clear() ; vVertices.push_back( m_mTree[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){ for ( int j : vNeigh ) vVertices.push_back( m_mTree[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){ for ( int j : vNeigh ) vVertices.push_back( m_mTree[j].GetBottomLeft()) ; } // se non l'ho già aggiunto tramite i vicini bottom aggiungo il punto bottom right else if ( ! bBottomRight ) { Point3d ptBr( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y) ; vVertices.push_back( ptBr) ; } vNeigh.clear() ; vVertices.push_back( m_mTree[nId].GetTopRight()) ; GetTopNeigh ( nId, vNeigh) ; 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) { for ( int j : vNeigh) vVertices.push_back( m_mTree[j].GetBottomLeft()) ; bTopLeft = true ; } else bTopLeft = false ; vNeigh.clear() ; GetLeftNeigh ( nId, vNeigh) ; 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) { for ( int j : vNeigh) vVertices.push_back( m_mTree[j].GetTopRight()) ; } // se non l'ho già aggiunto tramite i vicini top aggiungo il punto top left else if ( ! bTopLeft) { Point3d ptTl( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y) ; vVertices.push_back( ptTl) ; } vNeigh.clear() ; vVertices.push_back( m_mTree[nId].GetBottomLeft()) ; // se hop una cella con vicino dello stesso grado 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[nId].GetBottomLeft().x + m_mTree[nId].GetTopRight().x) / 2 ; dV = ( m_mTree[nId].GetBottomLeft().y + m_mTree[nId].GetTopRight().y) / 2 ; m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; m_pSrfBz->GetPointD1D2( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00) ; m_pSrfBz->GetPointD1D2( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10) ; m_pSrfBz->GetPointD1D2( m_mTree[nId].GetTopRight().x, m_mTree[nId].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11) ; m_pSrfBz->GetPointD1D2( m_mTree[nId].GetBottomLeft().x, m_mTree[nId].GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01) ; 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) > Dist(ptP10P01, ptPSrf)) { rotate(vVertices.begin(), vVertices.begin() + 1,vVertices.end()) ; vVertices.back() = vVertices[0] ; } } //double k = 0 ; m_vPolygons.emplace_back() ; for ( int i = 0 ; i < (int) vVertices.size() ; ++i ) { //k = double( i) / double( vVertices.size()) ; m_vPolygons.back().AddUPoint( i, vVertices[i]) ; } } } // restituisco i poligoni delle celle del tree nello spazio parametrico vPolygons = m_vPolygons ; return true ; }