//---------------------------------------------------------------------------- // EgalTech 2023 //---------------------------------------------------------------------------- // File : Kd-tree.cpp Data : 21.04.23 Versione : // Contenuto : Implementazione della classe kd-tree. // // // // Modifiche : 21.04.23 DB Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "KdTree.h" #include "SurfBezier.h" #include "GeoConst.h" using namespace std ; //---------------------------------------------------------------------------- Cell::Cell( void) : m_ptPbl( ORIG), m_ptPtr( ORIG), m_bProcessed ( false) , m_bSplitVert ( true) , m_dSplit( 0) , m_cTop ( nullptr), m_cBottom( nullptr), m_cLeft( nullptr), m_cRight ( nullptr), m_cParent( nullptr), m_cChild1( nullptr), m_cChild2( nullptr) {} //---------------------------------------------------------------------------- Cell::~Cell( void) { } //---------------------------------------------------------------------------- inline bool Cell::IsSame( std::shared_ptr cOtherCell) { if ( AreSamePointXYApprox( m_ptPbl, cOtherCell->GetBottomLeft()) && AreSamePointXYApprox( m_ptPtr, cOtherCell->GetTopRight())) { return true ; } else { return false ; } } //---------------------------------------------------------------------------- void Cell::Split( double dSplitValue) { m_dSplit = dSplitValue ; //Cell cChild1, cChild2, cParent, cTop, cBottom, cLeft, cRight ; m_cChild1 = make_shared() ; //m_cChild2 = &cChild2 ; //m_cParent = &cParent ; //m_cTop = &cTop ; //m_cBottom = &cBottom ; //m_cLeft = &cLeft ; //m_cRight = &cRight ; m_cChild2 = make_shared() ; //m_cParent = make_shared() ; //m_cTop = make_shared() ; //m_cBottom = make_shared() ; //m_cLeft = make_shared() ; //m_cRight = make_shared() ; if ( ! m_bSplitVert ) { // la cella figlio 1 è quella sopra Point3d ptBL( m_ptPbl.x, m_dSplit) ; m_cChild1->SetBottomLeft( ptBL) ; m_cChild1->SetTopRight( m_ptPtr) ; m_cChild1->m_cTop = m_cTop ; m_cChild1->m_cBottom = m_cChild2 ; m_cChild1->m_cLeft = m_cLeft ; m_cChild1->m_cRight = m_cRight ; //m_cChild1->m_cParent = make_shared(*this) ; Point3d ptTR( m_ptPtr.x, m_dSplit) ; m_cChild2->SetBottomLeft( m_ptPbl) ; m_cChild2->SetTopRight( ptTR) ; m_cChild2->m_cTop = m_cChild1 ; m_cChild2->m_cBottom = m_cBottom ; m_cChild2->m_cLeft = m_cLeft ; m_cChild2->m_cRight = m_cRight ; //m_cChild2->m_cParent = make_shared(*this) ; } else { // la cella figlio 1 è quella di sinistra Point3d ptTR( m_dSplit, m_ptPtr.y) ; m_cChild1->SetBottomLeft( m_ptPbl) ; m_cChild1->SetTopRight( ptTR) ; m_cChild1->m_cTop = m_cTop ; m_cChild1->m_cBottom = m_cBottom ; m_cChild1->m_cLeft = m_cLeft ; m_cChild1->m_cRight = m_cChild2 ; //m_cChild1->m_cParent = make_shared(*this) ; Point3d ptBL( m_dSplit, m_ptPbl.y) ; m_cChild2->SetBottomLeft( ptBL) ; m_cChild2->SetTopRight( m_ptPtr) ; m_cChild2->m_cTop = m_cTop ; m_cChild2->m_cBottom = m_cBottom ; m_cChild2->m_cLeft = m_cChild1 ; m_cChild2->m_cRight = m_cRight ; //m_cChild2->m_cParent = make_shared(*this) ; } //m_bProcessed = true ; } //---------------------------------------------------------------------------- bool //Cell::IsLeaf ( void) const Cell::IsLeaf ( void) { if( m_cChild1 == nullptr && m_cChild2 == nullptr) return true ; else return false ; } //---------------------------------------------------------------------------- KdTree::KdTree( void) : m_dLinTol(LIN_TOL_FINE), m_pSrfBz(nullptr), m_cRoot( make_shared()) {} //---------------------------------------------------------------------------- KdTree::KdTree( const SurfBezier* pSrfBz) : m_dLinTol( LIN_TOL_FINE), 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) ; Point3d ptTop( nSpanU, nSpanV) ; m_cRoot = make_shared() ; m_cRoot->SetBottomLeft( ORIG) ; m_cRoot->SetTopRight( ptTop) ; } //---------------------------------------------------------------------------- KdTree::~KdTree( void) { } //---------------------------------------------------------------------------- void KdTree::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) ; Point3d ptTop( nSpanU, nSpanV) ; m_cRoot = make_shared() ; m_cRoot->SetBottomLeft( ORIG) ; m_cRoot->SetTopRight( ptTop) ; } //---------------------------------------------------------------------------- bool KdTree::BuildTree( int nStepU, int nStepV) { // 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 err ; // errore calcolato double dist ; // distanza tra punti selezionati double dU, dV , dUmax, dVmax; Point3d ptBz, ptBl ; // cerco lo scostamento massimo tra la sup di Bezier e la sua approssimazione bilineare // shared_ptr cToSplit = make_shared(&m_cRoot) ; shared_ptr cToSplit = m_cRoot ; //while ( cToSplit != nullptr && // ( cToSplit->IsSame( &m_cRoot) || // per entrare nel ciclo // cToSplit->IsProcessed() == false || // per processare le child1 // ( ! cToSplit->IsSame( &m_cRoot) && cToSplit->m_cChild2->IsProcessed() == false))) { // per processare le child2 finché torno alla root int c = 1 ; while ( cToSplit != nullptr && cToSplit->IsProcessed() == false) { err = 0 ; // calcolo la bilineare per gli estremi della cella SurfBezier pSrfBl ; pSrfBl.Init(1, 1, 1, 1, false) ; m_pSrfBz->GetPointD1D2( cToSplit->GetBottomLeft().x, cToSplit->GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; pSrfBl.SetControlPoint( 0, ptBz) ; // P00 m_pSrfBz->GetPointD1D2( cToSplit->GetTopRight().x, cToSplit->GetBottomLeft().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; pSrfBl.SetControlPoint( 1, ptBz) ; // P01 m_pSrfBz->GetPointD1D2( cToSplit->GetBottomLeft().x, cToSplit->GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; pSrfBl.SetControlPoint( 2, ptBz) ; // P10 m_pSrfBz->GetPointD1D2( cToSplit->GetTopRight().x, cToSplit->GetTopRight().y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) ; pSrfBl.SetControlPoint( 3, ptBz) ; // P11 for ( int i = 1 ; i <= nStepU ; ++ i) { for ( int j = 1 ; j <= nStepV ; ++ j) { dU = double ( i) / nStepU * ( cToSplit->GetTopRight().y - cToSplit->GetBottomLeft().y) ; dV = double ( j) / nStepV * ( cToSplit->GetTopRight().x - cToSplit->GetBottomLeft().x) ; if ( ! m_pSrfBz->GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz) || ! pSrfBl.GetPointD1D2( dU, dV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBl)) return false ; dist = Dist( ptBz, ptBl) ; if ( dist > err) { // ### nelle condizioni dell'if probabilmente devo già controllare che la dimensione della cella non sia troppo piccola err = dist ; dUmax = dU ; dVmax = dV ; } } } // devo spostare la condizione sulla dimensione minima di una cella///////////////////////////////////////////////////////////////////// // probabilmente qui ### if ( err > m_dLinTol && ( dUmax - cToSplit->GetBottomLeft().y) >= 0.01 && ( dVmax - cToSplit->GetBottomLeft().x) >= 0.01) { // 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 orario rispetto al relativo vertice della patch Point3d ptPSrf, ptP00, ptP10, ptP11, ptP01; m_pSrfBz->GetPointD1D2( dUmax, dVmax, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPSrf) ; m_pSrfBz->GetPointD1D2( dUmax, 0, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP00) ; m_pSrfBz->GetPointD1D2( 1, dVmax, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP10) ; m_pSrfBz->GetPointD1D2( dUmax, 1, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP11) ; m_pSrfBz->GetPointD1D2( 0, dVmax, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptP01) ; Point3d ptP00P11 = ( 1 - dVmax) * ptP00 + dVmax * ptP11 ; Point3d ptP10P01 = ( 1 - dUmax) * ptP10 + dUmax * 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)) { cToSplit->SetSplitDirVert( false) ; cToSplit->Split( dUmax) ; } else { cToSplit->SetSplitDirVert( true) ; cToSplit->Split( dVmax) ; } cToSplit->m_cChild1->SetParent( cToSplit) ; cToSplit->m_cChild2->SetParent( cToSplit) ; // procedo con lo split del Child1 cToSplit = cToSplit->m_cChild1 ; } else { // sono arrivato ad una cella Leaf, quindi salvo il poligono cToSplit->Processed() ; Point3d ptPbr( cToSplit->GetTopRight().x, cToSplit->GetBottomLeft().y) ; Point3d ptPtl( cToSplit->GetBottomLeft().x, cToSplit->GetTopRight().y) ; m_vPolygons.emplace_back() ; m_vPolygons.back().AddUPoint(0, cToSplit->GetBottomLeft()) ; m_vPolygons.back().AddUPoint(0.25, ptPbr) ; m_vPolygons.back().AddUPoint(0.5, cToSplit->GetTopRight()) ; m_vPolygons.back().AddUPoint(0.75, ptPtl) ; m_vPolygons.back().AddUPoint(1, cToSplit->GetBottomLeft()) ; // risalgo i parent finché non trovo il primo Child2 da processare cToSplit = cToSplit->m_cParent ; if ( cToSplit->m_cChild1->IsProcessed() && cToSplit->m_cChild2->IsProcessed()) cToSplit->Processed() ; // questa condizione mi manda in un loop perché non processo mai il child2 di root // a'altro canto devo evitare di fare i passaggi successivi se sono già parent processato //if ( cToSplit->IsSame( &m_cRoot)) // continue ; while ( cToSplit->m_cChild2->IsProcessed()) { if ( cToSplit->m_cParent != nullptr ) cToSplit = cToSplit->m_cParent ; if ( cToSplit->m_cChild1->IsProcessed() && cToSplit->m_cChild2->IsProcessed()) cToSplit->Processed() ; if ( cToSplit->IsSame( m_cRoot) && cToSplit->m_cChild2->IsProcessed()) break ; } //if ( cToSplit->IsSame( &m_cRoot)) // continue ; //else // cToSplit = cToSplit->m_cChild2 ; cToSplit = cToSplit->m_cChild2 ; } c ++ ; } return true ; } //---------------------------------------------------------------------------- bool KdTree::GetPolygons( POLYLINEVECTOR& vPolygons) { // restituisco i poligoni delle celle del kd-tree nello spazio parametrico if ( m_vPolygons.empty()) return false ; vPolygons = m_vPolygons ; return true ; }