Files
EgtGeomKernel/DistPointSurfTm.cpp
T
Riccardo Elitropi 3a69dcfa79 EgtGeomKernel (2.7i3) :
- 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.
2025-09-12 17:01:14 +02:00

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