Files
EgtGeomKernel/KdTree.cpp
T
Daniele Bariletti 6a3fc0fd97 EgtGeomKernel :
- implementazione delle classi KdTree e Cell
- problemi : bilanciamento albero e uso puntatori
2023-05-03 12:09:58 +02:00

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 ;
}