//---------------------------------------------------------------------------- // EgalTech 2018-2020 //---------------------------------------------------------------------------- // File : DistPointSurfTm.cpp Data : 19.12.20 Versione : 2.2l3 // Contenuto : Implementazione della classe distanza Punto da Trimesh. // // // // Modifiche : 07.12.18 LM Creazione modulo. // // //---------------------------------------------------------------------------- #include "stdafx.h" #include "SurfTriMesh.h" #include "/EgtDev/Include/EGkDistPointTria.h" #include "/EgtDev/Include/EGkDistPointSurfTm.h" #include "/EgtDev/Include/EGkIntersLineTria.h" using namespace std ; //---------------------------------------------------------------------------- // Calcola la differenza fra i bounding-box A e B. // L'insieme differenza non è un bounding-box, ma è esprimibile come unione di al più sei bounding-box. // Se l'insieme differenza fra i box non ha misura nulla viene restituito true, false altrimenti. // I casi in cui non vengono trovati box di misura positiva sono quelli in cui o il box A è contenuto // nel box B; uno di questi si verifica se il box A è vuoto. // Nel vettore vBoxDiff vengono restituiti i box la cui unione costituisce la differenza fra A e B. static bool BoundingBoxDifference( const BBox3d& boxA, const BBox3d& boxB, BOXVECTOR& vBoxDiff) { // Svuoto il risultato vBoxDiff.clear() ; // Se box A vuoto, risultato vuoto if ( boxA.IsEmpty()) return false ; // Se box B vuoto o i box non si intersecano, risultato è ancora A BBox3d boxInt ; if ( boxB.IsSmall() || ! boxA.FindIntersection( boxB, boxInt)) { vBoxDiff.emplace_back( boxA) ; return true ; } // Recupero i punti estremi dei box A e Intersezione Point3d ptMinA, ptMaxA ; boxA.GetMinMax( ptMinA, ptMaxA) ; Point3d ptMinInt, ptMaxInt ; boxInt.GetMinMax( ptMinInt, ptMaxInt) ; // Sotto if ( ptMinInt.z - ptMinA.z > EPS_SMALL) { BBox3d boxD( ptMinA, Point3d( ptMaxA.x, ptMaxA.y, ptMinInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // Sopra if ( ptMaxA.z - ptMaxInt.z > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMinA.y, ptMaxInt.z), ptMaxA) ; vBoxDiff.emplace_back( boxD) ; } // Davanti if ( ptMinInt.y - ptMinA.y > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMinA.y, ptMinInt.z), Point3d( ptMaxA.x, ptMinInt.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // Dietro if ( ptMaxA.y - ptMaxInt.y > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMaxInt.y, ptMinInt.z), Point3d( ptMaxA.x, ptMaxA.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // Sinistra if ( ptMinInt.x - ptMinA.x > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMinInt.y, ptMinInt.z), Point3d( ptMinInt.x, ptMaxInt.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // Destra if ( ptMaxA.y - ptMaxInt.y > EPS_SMALL) { BBox3d boxD( Point3d( ptMaxInt.x, ptMinInt.y, ptMinInt.z), Point3d( ptMaxA.x, ptMaxInt.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // Risultato return ( ! vBoxDiff.empty()) ; } //---------------------------------------------------------------------------- DistPointSurfTm::DistPointSurfTm( const Point3d& ptP, const ISurfTriMesh& tmSurf) : m_dDist( -1), m_nMinDistTriaIndex( 0), m_bIsInside( false) { // Trimesh non valida if ( &tmSurf == nullptr || ! tmSurf.IsValid()) return ; // Calcolo la distanza Calculate( ptP, tmSurf) ; } //---------------------------------------------------------------------------- void DistPointSurfTm::Calculate( const Point3d& ptP, const ISurfTriMesh& tmSurf) { // Inizializzo distanza non calcolata m_dDist = - 1. ; // Vettore di indici dei triangoli più vicini inizialmente vuoto m_vnMinDistTriaIndex.clear() ; // Controllo se la superficie è chiusa m_bIsSurfClosed = tmSurf.IsClosed() ; // Lavoro con l'oggetto superficie trimesh di base const SurfTriMesh* pStm = GetBasicSurfTriMesh( &tmSurf) ; if ( pStm == nullptr) return ; // Recupero e verifico il box locale della superficie BBox3d b3Stm = pStm->GetAllTriaBox() ; if ( b3Stm.IsEmpty()) return ; // Cerco triangoli in box centrati sul punto dato di ampiezza crescente ed escludendo le parti già verificate. // Termino quando non trovo più triangoli che possano soddisfare la richiesta. Point3d ptMin, ptMax ; b3Stm.GetMinMax( ptMin, ptMax) ; double dDeltaLen = max( min( min( b3Stm.GetDimX(), b3Stm.GetDimY()), b3Stm.GetDimZ()) / 40., 20.) ; double dBoxHalfLenX = max( max( ptMin.x - ptP.x, ptP.x - ptMax.x), 0.) + dDeltaLen ; double dBoxHalfLenY = max( max( ptMin.y - ptP.y, ptP.y - ptMax.y), 0.) + dDeltaLen ; double dBoxHalfLenZ = max( max( ptMin.z - ptP.z, ptP.z - ptMax.z), 0.) + dDeltaLen ; // Considero anche il box precedente per poter analizzare solo il volume differenza tra i due BBox3d boxPPrev( ptP) ; BBox3d boxP( ptP, dBoxHalfLenX, dBoxHalfLenY, dBoxHalfLenZ) ; // Variabili distanza minima, indice del triangolo di distanza minima, punto di distanza minima double dMinDist = DBL_MAX ; int nMinDistTriaIndex = SVT_NULL ; Point3d ptMinDistPoint ; // Finché non si verifica la condizione di terminazione ingrandisco il box. pStm->ResetTempInts() ; bool bContinue = true ; // creazione del vettore dei triangoli più vicini a ptP vector> vTria ; // while ( bContinue) { // Calcolo il box differenza con il precedente per non esplorare parti già considerate BOXVECTOR vBox ; BoundingBoxDifference( boxP, boxPPrev, vBox) ; // Ciclo sui box differenza bool bCollide = false ; for ( const auto& b3Box : vBox) { // interseco il box con quello della superficie e ne verifico la distanza minima dal punto BBox3d b3Int ; if ( ! b3Box.FindIntersection( b3Stm, b3Int) || b3Int.DistFromPoint( ptP) > dMinDist) continue ; // ricerca sui triangoli nel box bCollide = true ; INTVECTOR vnIds ; if ( pStm->GetAllTriaOverlapBox( b3Int, vnIds)) { // Ciclo sui triangoli del sotto-box corrente for ( auto nT : vnIds) { int nTriaTemp ; Triangle3d trCurTria ; if ( pStm->GetTempInt( nT, nTriaTemp) && nTriaTemp == 0 && pStm->GetTriangle( nT, trCurTria)) { pStm->SetTempInt( nT, 1) ; DistPointTriangle distPT( ptP, trCurTria) ; double dCurrDist ; // Se la distanza del triangolo è valida e minore di quella attuale aggiorno if ( distPT.GetDist( dCurrDist)) { // se distanze uguali... if ( abs( dCurrDist - dMinDist) < EPS_SMALL) // aggiungo il triangolo vTria.emplace_back( make_pair( nT, trCurTria)) ; // se minore... else if ( dCurrDist < dMinDist) { // pulisco il vettore vTria.clear() ; dMinDist = dCurrDist ; nMinDistTriaIndex = nT ; distPT.GetMinDistPoint( ptMinDistPoint) ; // aggiungo il triangolo vTria.emplace_back( make_pair( nT, trCurTria)) ; } } } } } } // Se si verifica la condizione di terminazione arresto il ciclo altrimenti aggiorno i box if ( ! bCollide || dMinDist < EPS_SMALL) bContinue = false ; else { boxPPrev = boxP ; boxP.Expand( dDeltaLen) ; } } // se non ho trovato nessun triangolo, esco if ( nMinDistTriaIndex == SVT_NULL) return ; // Inizializzo il vettore dei triangoli a minima distanza for ( auto& Tria : vTria) m_vnMinDistTriaIndex.emplace_back( Tria.first) ; // salvo la distanza minima m_dDist = dMinDist ; // salvo il punto a distanza minima m_ptMinDistPoint = ptMinDistPoint ; // se il punto è sulla TriMesh... if ( m_dDist < EPS_SMALL) { m_nMinDistTriaIndex = nMinDistTriaIndex ; m_bIsInside = false ; return ; } // se ho un solo triangolo, allora deduco le informazioni da lui else if ( int( vTria.size()) == 1) { m_nMinDistTriaIndex = vTria.back().first ; m_bIsInside = ( ( ptP - m_ptMinDistPoint) * vTria.back().second.GetN() < - EPS_SMALL) ; return ; } // controllo se tutti i triangoli a minima distanza forniscono la stessa informazione // ( il punto potrebbe essere esterno a tutti, interno a tutti o indefinito ) bool bInside = false ; bool bOutside = false ; for ( int i = 0 ; i < int( vTria.size()) ; ++ i) { // scorro i triangoli a minima distanza if ( ( ptP - vTria[i].second.GetP( 0)) * vTria[i].second.GetN() < - EPS_SMALL) bInside = true ; else bOutside = true ; } // inizializzo le variabili membro m_nMinDistTriaIndex = nMinDistTriaIndex ; m_bIsInside = false ; // se le informazioni non sono coerenti, allora : // 1) calcolo i centroidi dei triangoli in questione // 2) ottengo il punto medio di questi centroidi // 3) controllo quale triangolo interseca il segmento che parte da ptP e arriva a tale punto // 4) userò questo triangolo per classificare ptP if ( bOutside == bInside) { // calcolo il baricentro complessivo Point3d ptBar_tot ; for ( auto& Tria : vTria) ptBar_tot += Tria.second.GetCentroid() ; ptBar_tot /= int( vTria.size()) ; // per ogni triangolo, cerco quello che interseca il segmento for ( auto& Tria : vTria) { Point3d ptInters1, ptInters2 ; int nType = IntersLineTria( ptP, ptBar_tot, Tria.second, ptInters1, ptInters2) ; if ( nType == ILTT_IN) { // se intersezione ho finito DistPointTriangle( ptP, Tria.second).GetMinDistPoint( m_ptMinDistPoint) ; m_bIsInside = ( ( ptP - m_ptMinDistPoint) * Tria.second.GetN() < - EPS_SMALL) ; m_nMinDistTriaIndex = Tria.first ; break ; } } } else // se informazioni coerenti m_bIsInside = bInside ; } //---------------------------------------------------------------------------- bool DistPointSurfTm::GetDist( double& dDist) const { // Distanza non valida if ( m_dDist < -EPS_ZERO) return false ; // Distanza valida dDist = m_dDist ; return true ; } //---------------------------------------------------------------------------- bool DistPointSurfTm::GetMinDistPoint( Point3d& ptMinDistPoint) const { // Distanza non valida if ( m_dDist < -EPS_ZERO) return false ; // Distanza valida ptMinDistPoint = m_ptMinDistPoint ; return true ; } //---------------------------------------------------------------------------- bool DistPointSurfTm::GetMinDistTriaIndex( int& nMinDistIndex) const { // Distanza non valida if ( m_dDist < -EPS_ZERO) return false ; // Distanza valida nMinDistIndex = m_nMinDistTriaIndex ; return true ; } //---------------------------------------------------------------------------- bool DistPointSurfTm::GetMinDistTriaIndices( INTVECTOR& vMinDistTriaIndex) const { // Distanza non valida if ( m_dDist < - EPS_ZERO) return false ; // Distanza valida vMinDistTriaIndex = m_vnMinDistTriaIndex ; return true ; } //---------------------------------------------------------------------------- int GetSurfTmNearestVertex( const Point3d& ptP, const ISurfTriMesh& tmSurf) { // Lavoro con l'oggetto superficie trimesh di base const SurfTriMesh* pStm = GetBasicSurfTriMesh( &tmSurf) ; if ( pStm == nullptr) return SVT_NULL ; // Recupero e verifico il box locale della superficie BBox3d b3Stm = pStm->GetAllTriaBox() ; if ( b3Stm.IsEmpty()) return SVT_NULL ; // Cerco triangoli in box centrati sul punto dato di ampiezza crescente ed escludendo le parti già verificate. // Termino quando non trovo più triangoli che possano soddisfare la richiesta. Point3d ptMin, ptMax ; b3Stm.GetMinMax( ptMin, ptMax) ; double dDeltaLen = max( min( min( b3Stm.GetDimX(), b3Stm.GetDimY()), b3Stm.GetDimZ()) / 40., 20.) ; double dBoxHalfLenX = max( max( ptMin.x - ptP.x, ptP.x - ptMax.x), 0.) + dDeltaLen ; double dBoxHalfLenY = max( max( ptMin.y - ptP.y, ptP.y - ptMax.y), 0.) + dDeltaLen ; double dBoxHalfLenZ = max( max( ptMin.z - ptP.z, ptP.z - ptMax.z), 0.) + dDeltaLen ; // Considero anche il box precedente per poter analizzare solo il volume differenza tra i due BBox3d boxPPrev( ptP) ; BBox3d boxP( ptP, dBoxHalfLenX, dBoxHalfLenY, dBoxHalfLenZ) ; // Variabili distanza minima int nVert = SVT_NULL ; double dMinSqDist = DBL_MAX ; // Finché non si verifica la condizione di terminazione ingrandisco il box. pStm->ResetTempInts() ; bool bContinue = true ; while ( bContinue) { // Calcolo il box differenza con il precedente per non esplorare parti già considerate BOXVECTOR vBox ; BoundingBoxDifference( boxP, boxPPrev, vBox) ; // Ciclo sui box differenza bool bCollide = false ; for ( const auto& b3Box : vBox) { // interseco il box con quello della superficie e ne verifico la distanza minima dal punto BBox3d b3Int ; if ( ! b3Box.FindIntersection( b3Stm, b3Int) || b3Int.SqDistFromPoint( ptP) > dMinSqDist) continue ; // ricerca sui triangoli nel box bCollide = true ; INTVECTOR vnIds ; if ( pStm->GetAllTriaOverlapBox( b3Int, vnIds)) { // Ciclo sui triangoli del sotto-box corrente for ( auto nT : vnIds) { int nTriaTemp ; int nIdVert[3] ; if ( pStm->GetTempInt( nT, nTriaTemp) && nTriaTemp == 0 && pStm->GetTriangle( nT, nIdVert)) { pStm->SetTempInt( nT, 1) ; for ( int i = 0 ; i < 3 ; ++ i) { Point3d ptVert ; if ( ! pStm->GetVertex( nIdVert[i], ptVert)) continue ; double dCurrSqDist = SqDist( ptP, ptVert) ; if ( dCurrSqDist < dMinSqDist) { dMinSqDist = dCurrSqDist ; nVert = nIdVert[i] ; } } } } } } // Se si verifica la condizione di terminazione arresto il ciclo altrimenti aggiorno i box if ( ! bCollide || dMinSqDist < EPS_SMALL * EPS_SMALL) bContinue = false ; else { boxPPrev = boxP ; boxP.Expand( dDeltaLen) ; } } return nVert ; }