3a69dcfa79
- Aggiunta funzione in DistPointSurfTm per ottenere i triangoli a minima distanza - Aggiunte funzioni per Offset Fillet di VolZmap e per Offset generici di superfici TriMesh aperte.
376 lines
15 KiB
C++
376 lines
15 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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<pair<int, Triangle3d>> vTria ; // <indice triangolo, Triangolo>
|
|
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 ;
|
|
}
|