6a3fc0fd97
- implementazione delle classi KdTree e Cell - problemi : bilanciamento albero e uso puntatori
286 lines
12 KiB
C++
286 lines
12 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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<Cell> 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<Cell>() ;
|
|
//m_cChild2 = &cChild2 ;
|
|
//m_cParent = &cParent ;
|
|
//m_cTop = &cTop ;
|
|
//m_cBottom = &cBottom ;
|
|
//m_cLeft = &cLeft ;
|
|
//m_cRight = &cRight ;
|
|
|
|
m_cChild2 = make_shared<Cell>() ;
|
|
//m_cParent = make_shared<Cell>() ;
|
|
//m_cTop = make_shared<Cell>() ;
|
|
//m_cBottom = make_shared<Cell>() ;
|
|
//m_cLeft = make_shared<Cell>() ;
|
|
//m_cRight = make_shared<Cell>() ;
|
|
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<Cell>(*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<Cell>(*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<Cell>(*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<Cell>(*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<Cell>())
|
|
{}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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<Cell>() ;
|
|
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<Cell>() ;
|
|
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<Cell> cToSplit = make_shared<Cell>(&m_cRoot) ;
|
|
shared_ptr<Cell> 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 ;
|
|
} |