Files
EgtGeomKernel/SurfTriMesh.cpp
Daniele Bariletti 7e62acf351 EgtGeomKernel :
- aggiunta funzione per richiedere Part e Shell cui appartiene una faccia.
2026-03-04 15:55:25 +01:00

4591 lines
157 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2014-2022
//----------------------------------------------------------------------------
// File : SurfTriMesh.cpp Data : 12.08.22 Versione : 2.4h1
// Contenuto : Implementazione della classe Superfici TriMesh.
//
//
//
// Modifiche : 26.03.14 DS Creazione modulo.
// 15.05.14 DS Corr. errore CreateByTwoCurves che dava loop infinito.
// 02.01.19 LM Aggiunta gestione connessione.
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "SurfTriMesh.h"
#include "GeoObjFactory.h"
#include "GdbGeo.h"
#include "NgeWriter.h"
#include "NgeReader.h"
#include "SurfFlatRegion.h"
#include "Triangulate.h"
#include "GeoConst.h"
#include "/EgtDev/Include/EGkDistPointLine.h"
#include "/EgtDev/Include/EGkDistLineLine.h"
#include "/EgtDev/Include/EGkDistPointSurfTm.h"
#include "/EgtDev/Include/EGkIntersLinePlane.h"
#include "/EgtDev/Include/EGkPointGrid3d.h"
#include "/EgtDev/Include/EGkPolygon3d.h"
#include "/EgtDev/Include/EGkPolyLine.h"
#include "/EgtDev/Include/EGkStringUtils3d.h"
#include "/EgtDev/Include/EGkUiUnits.h"
#include "/EgtDev/Include/EGkSfrCreate.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
#include <set>
#include <tuple>
using namespace std ;
//----------------------------------------------------------------------------
GEOOBJ_REGISTER( SRF_TRIMESH, NGE_S_TRM, SurfTriMesh) ;
//----------------------------------------------------------------------------
SurfTriMesh::SurfTriMesh( void)
: m_nStatus( TO_VERIFY), m_dLinTol( STM_STD_LIN_TOL), m_dBoundaryAng( STM_STD_BOUNDARY_ANG),
m_dSmoothAng( STM_STD_SMOOTH_ANG), m_bShowEdges( false), m_bOriented( false), m_bClosed( false),
m_bFaceted( false), m_bFacEdged( false), m_nTimeStamp( 0), m_nTempProp{0,0}, m_dTempParam{0,0},
m_nMaxTFlag( 0), m_nShells( -1), m_pHGrd3d( nullptr)
{
m_dCosBndAng = cos( m_dBoundaryAng * DEGTORAD) ;
m_dCosSmAng = cos( m_dSmoothAng * DEGTORAD) ;
}
//----------------------------------------------------------------------------
SurfTriMesh::~SurfTriMesh( void)
{
ResetHashGrids3d() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Init( int nNumVert, int nNumTria, int nNumFacet)
{
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Clear() ;
ResetHashGrids3d() ;
// se superficie vuota
if ( nNumVert == 0 && nNumTria == 0 && nNumFacet == 0)
return true ;
// verifico validità parametri
if ( nNumVert < 3 || nNumTria < 1)
return false ;
// prealloco la memoria
try {
m_vVert.reserve( nNumVert) ;
m_vTria.reserve( nNumTria) ;
if ( nNumFacet > 0)
m_vFacet.reserve( nNumFacet) ;
}
catch (...) {
return false ;
}
// completo inizializzazione
m_nStatus = OK ;
m_bClosed = false ;
m_nMaxTFlag = 0 ;
m_nShells = -1 ;
m_vPart.clear() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Clear( void)
{
m_nStatus = OK ;
m_dLinTol = STM_STD_LIN_TOL ;
m_dBoundaryAng = STM_STD_BOUNDARY_ANG ;
m_dCosBndAng = cos( m_dBoundaryAng * DEGTORAD) ;
m_dSmoothAng = STM_STD_SMOOTH_ANG ;
m_dCosSmAng = cos( m_dSmoothAng * DEGTORAD) ;
m_bShowEdges = false ;
m_bOriented = false ;
m_bClosed = false ;
m_bFaceted = false ;
m_bFacEdged = false ;
m_vVert.clear() ;
m_vTria.clear() ;
m_vFacet.clear() ;
m_OGrMgr.Clear() ;
ResetHashGrids3d() ;
m_nTimeStamp = 0 ;
m_nTempProp[0] = 0 ;
m_nTempProp[1] = 0 ;
m_dTempParam[0] = 0 ;
m_dTempParam[1] = 0 ;
m_nMaxTFlag = 0 ;
m_nShells = -1 ;
m_vPart.clear() ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::AddVertex( const Point3d& ptVert, double dU, double dV)
{
// imposto ricalcolo
m_nStatus = TO_VERIFY ;
m_nShells = -1 ;
m_vPart.clear() ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// inserisco il vertice
try { m_vVert.emplace_back( ptVert) ;}
catch(...) { return SVT_NULL ;}
// ne determino l'indice
int nId = int( m_vVert.size() - 1) ;
// aggiugo le coordinate corrispondenti allo spazio parametrico
m_vVert[nId].dU = dU ;
m_vVert[nId].dV = dV ;
return nId ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::MoveVertex( int nInd, const Point3d& ptNewVert)
{
// verifico validità indice
if ( nInd < 0 || nInd >= int( m_vVert.size()))
return false ;
// verifico non sia già cancellato
if ( m_vVert[nInd].nIdTria == SVT_DEL)
return false ;
// se non c'è spostamento, posso uscire
if ( AreSamePointExact( m_vVert[nInd].ptP, ptNewVert))
return true ;
// sposto il vertice
m_vVert[nInd].ptP = ptNewVert ;
// imposto ricalcolo
m_nStatus = TO_VERIFY ;
m_nShells = - 1 ;
m_vPart.clear() ;
m_bFaceted = false ;
m_bFacEdged = false ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// per aggiornare completamente la superficie chiamare DoCompacting
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::SetVertex( int nInd, const StmVert& vV)
{
// imposto ricalcolo
m_nStatus = TO_VERIFY ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// recupero la dimensione originale
int nPrevSize = int( m_vVert.size()) ;
// determino la dimensione necessaria
int nNewSize = max( nInd + 1, nPrevSize) ;
// se necessaria dimensione maggiore
if ( nNewSize > nPrevSize) {
// espando vettore
try { m_vVert.resize( nNewSize) ; }
catch (...) { return false ; }
// inizializzo a cancellati gli eventuali vertici intermedi
if ( ( nNewSize - nPrevSize) > 1) {
StmVert vEmpty( Point3d(), SVT_DEL, 0) ;
for ( int i = nPrevSize ; i < nNewSize ; ++ i)
m_vVert[i] = vEmpty ;
}
}
// inserisco il vertice
m_vVert[nInd] = vV ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::AddTriangle( const int nIdVert[3], int nTFlag)
{
// verifico che i tre indici siano diversi
if ( nIdVert[0] == nIdVert[1] ||
nIdVert[1] == nIdVert[2] ||
nIdVert[2] == nIdVert[0])
return SVT_DEL ;
// verifico che i tre vertici siano ben definiti
if ( nIdVert[0] < 0 || nIdVert[0] >= int( m_vVert.size()) ||
m_vVert[nIdVert[0]].nIdTria == SVT_DEL ||
nIdVert[1] < 0 || nIdVert[1] >= int( m_vVert.size()) ||
m_vVert[nIdVert[1]].nIdTria == SVT_DEL ||
nIdVert[2] < 0 || nIdVert[2] >= int( m_vVert.size()) ||
m_vVert[nIdVert[2]].nIdTria == SVT_DEL)
return SVT_DEL ;
// verifico che la normale sia ben definita
// calcolo vettori come due lati consecutivi del triangolo
Vector3d vtV1 = m_vVert[nIdVert[1]].ptP - m_vVert[nIdVert[0]].ptP ;
Vector3d vtV2 = m_vVert[nIdVert[2]].ptP - m_vVert[nIdVert[1]].ptP ;
// normale da prodotto vettoriale
Vector3d vtN = vtV1 ^ vtV2 ;
double dSqN = vtN.SqLen() ;
if ( dSqN < SQ_EPS_ZERO)
return SVT_DEL ;
// verifico la distanza minima tra i vertici e i lati opposti
if ( dSqN < SQ_EPS_TRIA_H * max( { vtV1.SqLen(), vtV2.SqLen(), ( vtV1 + vtV2).SqLen()}))
return SVT_DEL ;
// imposto ricalcolo
m_nStatus = TO_VERIFY ;
m_nShells = -1 ;
m_vPart.clear() ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
if ( ! vtN.Normalize( EPS_ZERO))
return SVT_DEL ;
// inserisco il triangolo
try { m_vTria.emplace_back( nIdVert, nTFlag) ;}
catch(...) { return SVT_NULL ;}
// aggiorno la sua normale
m_vTria.back().vtN = vtN ;
// aggiorno massimo TFlag
m_nMaxTFlag = max( m_nMaxTFlag, nTFlag) ;
// ne determino l'indice
return int( m_vTria.size() - 1) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::SetTriangle( int nInd, const StmTria& tT)
{
// imposto ricalcolo
m_nStatus = TO_VERIFY ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// recupero la dimensione originale
int nPrevSize = int( m_vTria.size()) ;
// determino la dimensione necessaria
int nNewSize = max( nInd + 1, nPrevSize) ;
// se necessaria dimensione maggiore
if ( nNewSize > nPrevSize) {
// espando vettore
try { m_vTria.resize( nNewSize) ; }
catch (...) { return false ; }
// inizializzo a cancellati gli eventuali triangoli intermedi
if ( ( nNewSize - nPrevSize) > 1) {
StmTria tEmpty ;
tEmpty.nIdVert[0] = SVT_DEL ;
for ( int i = nPrevSize ; i < nNewSize ; ++ i)
m_vTria[i] = tEmpty ;
}
}
// inserisco il triangolo
m_vTria[nInd] = tT ;
// aggiorno massimo TFlag
m_nMaxTFlag = max( m_nMaxTFlag, tT.nTFlag) ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::RemoveTriangle( int nId)
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize())
return false ;
// verifico se già cancellato
if ( m_vTria[nId].nIdVert[0] == SVT_DEL)
return true ;
// aggiorno eventuali riferimenti dei vertici
for ( int i = 0 ; i < 3 ; ++ i) {
// indice vertice
int nV = m_vTria[nId].nIdVert[i] ;
// se vertice non c'è passo al prossimo
if ( nV < 0 || nV >= int( m_vVert.size()))
continue ;
if ( m_vVert[nV].nIdTria == nId) {
int nAdjP = m_vTria[nId].nIdAdjac[i] ;
int nAdjM = m_vTria[nId].nIdAdjac[(i+2)%3] ;
if ( nAdjP != SVT_NULL && m_vTria[nAdjP].nIdVert[0] != SVT_DEL)
m_vVert[nV].nIdTria = nAdjP ;
else if ( nAdjM != SVT_NULL && m_vTria[nAdjM].nIdVert[0] != SVT_DEL)
m_vVert[nV].nIdTria = nAdjM ;
else
m_vVert[nV].nIdTria = SVT_NULL ;
}
}
// annullo le adiacenze dei triangoli vicini a questo
for ( int i = 0 ; i < 3 ; ++ i) {
// indice triangolo adiacente
int nAdjT = m_vTria[nId].nIdAdjac[i] ;
// se triangolo adiacente non c'è passo al prossimo
if ( nAdjT == SVT_NULL || m_vTria[nAdjT].nIdVert[0] == SVT_DEL)
continue ;
// ne sistemo la contro-adiacenza
for ( int j = 0 ; j < 3 ; ++ j) {
if ( m_vTria[nAdjT].nIdAdjac[j] == nId)
m_vTria[nAdjT].nIdAdjac[j] = SVT_NULL ;
}
}
// dichiaro cancellato il triangolo
m_vTria[nId].nIdVert[0] = SVT_DEL ;
// invalido calcolo facce e loro bordi
m_bFaceted = false ;
m_bFacEdged = false ;
// invalido calcolo connettività
m_nShells = -1 ;
m_vPart.clear() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetArea( double& dArea) const
{
// controllo parametro di ritorno
if ( &dArea == nullptr)
return false ;
// inizio con area nulla
dArea = 0 ;
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// sommo l'area di tutti i triangoli
Triangle3d Tria ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
dArea += Tria.GetArea() ;
nId = GetNextTriangle( nId, Tria) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetVolume( double& dVolume) const
{
// controllo parametro di ritorno
if ( &dVolume == nullptr)
return false ;
// inizio con volume nullo
dVolume = 0 ;
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// la superficie deve essere chiusa
if ( ! IsClosed())
return true ;
// sommo il volume con segno di tutte le piramidi dall'origine ad ogni faccia
Triangle3d Tria ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
Vector3d vtA = ( Tria.GetP( 1) - Tria.GetP( 0)) ^ ( Tria.GetP( 2) - Tria.GetP( 0)) ;
dVolume += ( Tria.GetP( 0) - ORIG) * vtA ;
nId = GetNextTriangle( nId, Tria) ;
}
dVolume /= 6 ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetCentroid( Point3d& ptCen) const
{
// controllo parametro di ritorno
if ( &ptCen == nullptr)
return false ;
// inizio con centro nell'origine
ptCen = ORIG ;
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// se la superficie è chiusa, calcolo il centroide del solido
if ( IsClosed()) {
// applico le formule di R. Nurnberg Imperial College London ad ogni faccia
Triangle3d Tria ;
double dVolume = 0 ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
Vector3d vtA = ( Tria.GetP( 1) - Tria.GetP( 0)) ^ ( Tria.GetP( 2) - Tria.GetP( 0)) ;
dVolume += ( Tria.GetP( 0) - ORIG) * vtA ;
ptCen.x += vtA.x * (( Tria.GetP( 0).x + Tria.GetP( 1).x) * ( Tria.GetP( 0).x + Tria.GetP( 1).x) +
( Tria.GetP( 1).x + Tria.GetP( 2).x) * ( Tria.GetP( 1).x + Tria.GetP( 2).x) +
( Tria.GetP( 2).x + Tria.GetP( 0).x) * ( Tria.GetP( 2).x + Tria.GetP( 0).x)) ;
ptCen.y += vtA.y * (( Tria.GetP( 0).y + Tria.GetP( 1).y) * ( Tria.GetP( 0).y + Tria.GetP( 1).y) +
( Tria.GetP( 1).y + Tria.GetP( 2).y) * ( Tria.GetP( 1).y + Tria.GetP( 2).y) +
( Tria.GetP( 2).y + Tria.GetP( 0).y) * ( Tria.GetP( 2).y + Tria.GetP( 0).y)) ;
ptCen.z += vtA.z * (( Tria.GetP( 0).z + Tria.GetP( 1).z) * ( Tria.GetP( 0).z + Tria.GetP( 1).z) +
( Tria.GetP( 1).z + Tria.GetP( 2).z) * ( Tria.GetP( 1).z + Tria.GetP( 2).z) +
( Tria.GetP( 2).z + Tria.GetP( 0).z) * ( Tria.GetP( 2).z + Tria.GetP( 0).z)) ;
nId = GetNextTriangle( nId, Tria) ;
}
dVolume /= 6 ;
if ( abs( dVolume) < EPS_SMALL * EPS_SMALL * EPS_SMALL)
return false ;
ptCen /= ( 24 * 2 * dVolume) ;
}
// altrimenti calcolo il centroide della superficie
else {
Triangle3d Tria ;
double dArea = 0 ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
double dTriaArea = Tria.GetArea() ;
dArea += dTriaArea ;
ptCen += Tria.GetCentroid() * dTriaArea ;
nId = GetNextTriangle( nId, Tria) ;
}
if ( abs( dArea) < SQ_EPS_SMALL)
return false ;
ptCen /= dArea ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::IsEmpty( void) const
{
// verifico presenza di almeno un vertice
bool bVertFound = false ;
for ( int nId = 0 ; nId < GetVertexSize() && ! bVertFound ; ++ nId) {
if ( m_vVert[nId].nIdTria != SVT_DEL)
bVertFound = true ;
}
if ( ! bVertFound)
return true ;
// verifico presenza di almeno un triangolo
bool bTriaFound = false ;
for ( int nId = 0 ; nId < GetTriangleSize() && ! bTriaFound ; ++ nId) {
if ( m_vTria[nId].nIdVert[0] != SVT_DEL)
bTriaFound = true ;
}
return ( ! bTriaFound) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetVertexCount( void) const
{
// calcolo il numero dei vertici cancellati
int nErased = 0 ;
for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) {
if ( m_vVert[nId].nIdTria == SVT_DEL)
++ nErased ;
}
return ( GetVertexSize() - nErased) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetTriangleCount( void) const
{
// calcolo il numero dei triangoli validi
int nCount = 0 ;
for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) {
if ( m_vTria[nId].nIdVert[0] != SVT_DEL)
++ nCount ;
}
return nCount ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetTriangleCount( int nTFlag) const
{
// calcolo il numero dei triangoli con flag indicato e non cancellati
int nCount = 0 ;
for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) {
if ( m_vTria[nId].nIdVert[0] != SVT_DEL && m_vTria[nId].nTFlag == nTFlag)
++ nCount ;
}
return nCount ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetVertex( int nId, Point3d& ptP) const
{
// verifico esistenza del vertice
if ( nId < 0 || nId >= GetVertexSize() || m_vVert[nId].nIdTria == SVT_DEL)
return false ;
// recupero i dati
ptP = m_vVert[nId].ptP ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetVertexParam( int nId, double& dU, double& dV) const
{
// verifico esistenza del vertice
if ( nId < 0 || nId >= GetVertexSize() || m_vVert[nId].nIdTria == SVT_DEL)
return false ;
// recupero i dati (verso l'esterno sempre in 0..1..2..3..)
dU = m_vVert[nId].dU / PREC_SCALE_COEFF ;
dV = m_vVert[nId].dV / PREC_SCALE_COEFF ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetFirstVertex( Point3d& ptP) const
{
return GetNextVertex( SVT_NULL, ptP) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetNextVertex( int nId, Point3d& ptP) const
{
// cerco il primo successivo valido
do {
nId ++ ;
} while ( nId < GetVertexSize() && m_vVert[nId].nIdTria == SVT_DEL) ;
// se indice non valido
if ( nId < 0 || nId >= GetVertexSize())
return SVT_NULL ;
// recupero i dati
ptP = m_vVert[nId].ptP ;
// ritorno indice triangolo corrente
return nId ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangle( int nId, int nIdVert[3]) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
// recupero i dati
nIdVert[0] = m_vTria[nId].nIdVert[0] ;
nIdVert[1] = m_vTria[nId].nIdVert[1] ;
nIdVert[2] = m_vTria[nId].nIdVert[2] ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetFirstTriangle( int nIdVert[3]) const
{
return GetNextTriangle( SVT_NULL, nIdVert) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetNextTriangle( int nId, int nIdVert[3]) const
{
// cerco il primo successivo valido
do {
nId ++ ;
} while ( nId < GetTriangleSize() && m_vTria[nId].nIdVert[0] == SVT_DEL) ;
// se indice non valido
if ( nId < 0 || nId >= GetTriangleSize())
return SVT_NULL ;
// recupero i dati
nIdVert[0] = m_vTria[nId].nIdVert[0] ;
nIdVert[1] = m_vTria[nId].nIdVert[1] ;
nIdVert[2] = m_vTria[nId].nIdVert[2] ;
// ritorno indice triangolo corrente
return nId ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangle( int nId, Triangle3d& Tria) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
// recupero i dati
Tria.Set( m_vVert[m_vTria[nId].nIdVert[0]].ptP,
m_vVert[m_vTria[nId].nIdVert[1]].ptP,
m_vVert[m_vTria[nId].nIdVert[2]].ptP,
m_vTria[nId].vtN) ;
Tria.SetGrade( m_vTria[nId].nTFlag) ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetFirstTriangle( Triangle3d& Tria) const
{
return GetNextTriangle( SVT_NULL, Tria) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetNextTriangle( int nId, Triangle3d& Tria) const
{
// cerco il primo successivo valido
do {
nId ++ ;
} while ( nId < GetTriangleSize() && m_vTria[nId].nIdVert[0] == SVT_DEL) ;
// se indice non valido
if ( nId < 0 || nId >= GetTriangleSize())
return SVT_NULL ;
// recupero i dati
Tria.Set( m_vVert[m_vTria[nId].nIdVert[0]].ptP,
m_vVert[m_vTria[nId].nIdVert[1]].ptP,
m_vVert[m_vTria[nId].nIdVert[2]].ptP,
m_vTria[nId].vtN) ;
Tria.SetGrade( m_vTria[nId].nTFlag) ;
// ritorno indice triangolo corrente
return nId ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangle( int nId, Triangle3dEx& Tria) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
// recupero i dati
Tria.Set( m_vVert[m_vTria[nId].nIdVert[0]].ptP,
m_vVert[m_vTria[nId].nIdVert[1]].ptP,
m_vVert[m_vTria[nId].nIdVert[2]].ptP,
m_vTria[nId].vtN) ;
Tria.SetGrade( m_vTria[nId].nTFlag) ;
GetTriangleBoundaryEdges( nId, const_cast<TriFlags3d&>( Tria.GetTriFlags())) ;
GetTriangleSmoothNormals( nId, const_cast<TriNormals3d&>( Tria.GetTriNormals())) ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetFirstTriangle( Triangle3dEx& Tria) const
{
return GetNextTriangle( SVT_NULL, Tria) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetNextTriangle( int nId, Triangle3dEx& Tria) const
{
// cerco il primo successivo valido
do {
nId ++ ;
} while ( nId < GetTriangleSize() && m_vTria[nId].nIdVert[0] == SVT_DEL) ;
// se indice non valido
if ( nId < 0 || nId >= GetTriangleSize())
return SVT_NULL ;
// recupero i dati
Tria.Set( m_vVert[m_vTria[nId].nIdVert[0]].ptP,
m_vVert[m_vTria[nId].nIdVert[1]].ptP,
m_vVert[m_vTria[nId].nIdVert[2]].ptP,
m_vTria[nId].vtN) ;
Tria.SetGrade( m_vTria[nId].nTFlag) ;
GetTriangleBoundaryEdges( nId, const_cast<TriFlags3d&>( Tria.GetTriFlags())) ;
GetTriangleSmoothNormals( nId, const_cast<TriNormals3d&>( Tria.GetTriNormals())) ;
// ritorno indice triangolo corrente
return nId ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetAllTriaAroundVertex( int nV, INTVECTOR& vT, bool& bCirc) const
{
const int MAX_VT_SIZE = 512 ;
// pulisco il vettore risultato
vT.clear() ;
// verifico esistenza del vertice
if ( nV < 0 || nV >= GetVertexSize() || m_vVert[nV].nIdTria == SVT_DEL)
return 0 ;
// recupero il triangolo puntato dal vertice
int nT = m_vVert[nV].nIdTria ;
if ( nT == SVT_NULL)
return 0 ;
vT.push_back( nT) ;
// cerco i triangoli adiacenti con lo stesso vertice in CCW
int k ;
int nTa = nT ;
do {
// ricerco triangolo
if ( FindVertexInTria( nV, nTa, k))
nTa = m_vTria[nTa].nIdAdjac[Prev(k)] ;
else
nTa = SVT_NULL ;
// se non valido, esco
if ( nTa == nT || nTa == SVT_NULL)
break ;
// per evitare cicli infiniti dovuti a triangoli invertiti
if ( find( vT.begin(), vT.end(), nTa) != vT.end())
break ;
// inserisco in elenco
vT.push_back( nTa) ;
} while ( vT.size() < MAX_VT_SIZE) ;
// se sono ritornato al triangolo di partenza ho fatto un giro e concluso la ricerca
if ( nTa == nT) {
bCirc = true ;
return int( vT.size()) ;
}
// altrimenti, devo cercare i triangoli adiacenti con lo stesso vertice in CW
nTa = nT ;
do {
// ricerco triangolo
if ( FindVertexInTria( nV, nTa, k))
nTa = m_vTria[nTa].nIdAdjac[k] ;
else
nTa = SVT_NULL ;
// se non valido, esco
if ( nTa == nT || nTa == SVT_NULL)
break ;
// per evitare cicli infiniti dovuti a triangoli invertiti
if ( find( vT.begin(), vT.end(), nTa) != vT.end())
break ;
// inserisco in elenco
vT.insert( vT.begin(), nTa) ;
} while ( vT.size() < MAX_VT_SIZE) ;
bCirc = ( nTa == nT) ;
return int( vT.size()) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetVertexSmoothNormal( int nV, int nT, Vector3d& vtN) const
{
// verifico esistenza del vertice
if ( nV < 0 || nV >= GetVertexSize() || m_vVert[nV].nIdTria == SVT_DEL)
return false ;
// verifico esistenza del triangolo
if ( nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL)
return false ;
// recupero la normale del vertice
for ( int i = 0 ; i < 3 ; ++ i) {
if ( nV == m_vTria[nT].nIdVert[i]) {
if ( ! GetTriangleSmoothNormal( nT, i, vtN))
vtN = m_vTria[nT].vtN ;
return true ;
}
}
// il vertice non appartiene al triangolo
return false ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangleBoundaryEdges( int nId, TriFlags3d& TFlags) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
// verifico i tre lati
for ( int i = 0 ; i < 3 ; ++ i) {
// indice triangolo adiacente al lato
int nT = m_vTria[nId].nIdAdjac[i] ;
// se già definite le facce, verifico indice faccia
if ( m_bFaceted) {
TFlags.bFlag[i] = ( nT == SVT_NULL || m_vTria[nId].nIdFacet != m_vTria[nT].nIdFacet) ;
}
// altrimenti verifico con le normali
else
// se non c'è triangolo adiacente o se forma un angolo oltre il limite, il lato è un contorno
TFlags.bFlag[i] = ( nT == SVT_NULL || m_vTria[nId].vtN * m_vTria[nT].vtN < m_dCosBndAng) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangleSmoothNormals( int nId, TriNormals3d& TNrms) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
// recupero le normali di ciascun vertice
for ( int i = 0 ; i < 3 ; ++ i) {
if ( ! GetTriangleSmoothNormal( nId, i, TNrms.vtN[i]))
TNrms.vtN[i] = m_vTria[nId].vtN ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangleAdjacencies( int nId, int nIdAdjTriaId[3]) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
// recupero gli indici dei triangoli adiacenti
for ( int i = 0 ; i < 3 ; ++ i) {
nIdAdjTriaId[i] = m_vTria[nId].nIdAdjac[i] ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTFlag( int nTriaId, int& nTFlag) const
{
// verifico esistenza del triangolo
if ( nTriaId < 0 || nTriaId >= GetTriangleSize() || m_vTria[nTriaId].nIdVert[0] == SVT_DEL)
return false ;
nTFlag = m_vTria[nTriaId].nTFlag ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTempInt( int nTriaId, int& nTempInt) const
{
// verifico esistenza del triangolo
if ( nTriaId < 0 || nTriaId >= GetTriangleSize() || m_vTria[nTriaId].nIdVert[0] == SVT_DEL)
return false ;
nTempInt = m_vTria[nTriaId].nTemp ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangleSmoothNormal( int nT, int nV, Vector3d& vtN) const
{
// recupero tutti i triangoli attorno al vertice
bool bCirc ;
INTVECTOR vT ;
int nTria = GetAllTriaAroundVertex( m_vTria[nT].nIdVert[nV], vT, bCirc) ;
if ( nTria < 1)
return false ;
// cerco indice del triangolo corrente
int nPos = - 1 ;
for ( int i = 0 ; i < int( vT.size()) ; ++ i) {
if ( vT[i] == nT) {
nPos = i ;
break ;
}
}
if ( nPos == -1)
return false ;
// medio le normali, finchè non incontro degli spigoli
double dAngW = 1 ;
GetTriangleVertexAngle( nT, nV, dAngW) ;
vtN = dAngW * m_vTria[nT].vtN ;
// parto dal triangolo e vado in direzione positiva
int nLim = nPos ;
for ( int i = NextIndAroundVertex( nPos, nTria, bCirc) ;
i != nPos && i < int( vT.size()) ;
i = NextIndAroundVertex( i, nTria, bCirc)) {
if ( m_vTria[nT].vtN * m_vTria[vT[i]].vtN >= m_dCosSmAng) {
int nK ; double dAngW = 1 ;
if ( FindVertexInTria( m_vTria[nT].nIdVert[nV], vT[i], nK) &&
GetTriangleVertexAngle( vT[i], nK, dAngW))
vtN += dAngW * m_vTria[vT[i]].vtN ;
}
else
break ;
nLim = i ;
}
// parto dal triangolo e vado in direzione negativa
for ( int i = PrevIndAroundVertex( nPos, nTria, bCirc) ;
i != nLim && i >= 0 ;
i = PrevIndAroundVertex( i, nTria, bCirc)) {
if ( m_vTria[nT].vtN * m_vTria[vT[i]].vtN >= m_dCosSmAng) {
int nK ; double dAngW = 1 ;
if ( FindVertexInTria( m_vTria[nT].nIdVert[nV], vT[i], nK) &&
GetTriangleVertexAngle( vT[i], nK, dAngW))
vtN += dAngW * m_vTria[vT[i]].vtN ;
}
else
break ;
}
vtN.Normalize() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriangleVertexAngle( int nT, int nV, double& dAng) const
{
// verifico esistenza del triangolo e validità vertice
if ( nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL || nV < 0 || nV > 2)
return false ;
// determino i vettori dei due lati che partono dal vertice
Point3d ptVert = m_vVert[ m_vTria[nT].nIdVert[nV]].ptP ;
Point3d ptPrev = m_vVert[ m_vTria[nT].nIdVert[Prev( nV)]].ptP ;
Point3d ptNext = m_vVert[ m_vTria[nT].nIdVert[Next( nV)]].ptP ;
Vector3d vtPrev = ptPrev - ptVert ;
Vector3d vtNext = ptNext - ptVert ;
// determino l'angolo tra i due lati
return vtPrev.GetAngle( vtNext, dAng) ;
}
//----------------------------------------------------------------------------
SurfTriMesh*
SurfTriMesh::CloneTriangle( int nT) const
{
// verifico validità superficie ed esistenza del triangolo
if ( ! IsValid() || nT < 0 || nT >= GetTriangleSize() || m_vTria[nT].nIdVert[0] == SVT_DEL)
return nullptr ;
// Creo nuovo oggetto SurfTriMesh
PtrOwner<SurfTriMesh> pSurfTM( new( nothrow) SurfTriMesh) ;
if ( IsNull( pSurfTM))
return nullptr ;
// Copio il valore dei membri
pSurfTM->m_dLinTol = m_dLinTol ;
pSurfTM->m_dBoundaryAng = m_dBoundaryAng ;
pSurfTM->m_dCosBndAng = m_dCosBndAng ;
pSurfTM->m_dSmoothAng = m_dSmoothAng ;
pSurfTM->m_dCosSmAng = m_dCosSmAng ;
// Copio il triangolo
int nNewInd[3] = { pSurfTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[0]].ptP),
pSurfTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[1]].ptP),
pSurfTM->AddVertex( m_vVert[m_vTria[nT].nIdVert[2]].ptP)} ;
if ( pSurfTM->AddTriangle( nNewInd, m_vTria[nT].nTFlag) == SVT_NULL)
return nullptr ;
// Aggiusto la superficie
pSurfTM->DoCompacting() ;
// Restituisco la nuova superficie
return Release( pSurfTM) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetLoops( POLYLINEVECTOR& vPL) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// aggiorno timestamp dei triangoli
++ m_nTimeStamp ;
for ( auto& Tria : m_vTria)
Tria.nTemp = m_nTimeStamp ;
// incremento time stamp
++ m_nTimeStamp ;
// ciclo sui triangoli
for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) {
// se triangolo valido e non ancora visitato
if ( m_vTria[nT].nIdVert[0] != SVT_DEL && m_vTria[nT].nTemp != m_nTimeStamp) {
// determino i triangoli adiacenti
int nAdjT[3] ;
for ( int j = 0 ; j < 3 ; ++ j)
nAdjT[j] = m_vTria[nT].nIdAdjac[j] ;
// se tutti e tre i lati sono di contorno
if ( nAdjT[0] == SVT_NULL &&
nAdjT[1] == SVT_NULL &&
nAdjT[2] == SVT_NULL) {
// ho trovato un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
}
// se i due lati 0 e 1 sono di contorno
else if ( nAdjT[0] == SVT_NULL &&
nAdjT[1] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back()))
return false ;
}
// se i due lati 1 e 2 sono di contorno
else if ( nAdjT[1] == SVT_NULL &&
nAdjT[2] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back()))
return false ;
}
// se i due lati 2 e 0 sono di contorno
else if ( nAdjT[2] == SVT_NULL &&
nAdjT[0] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back()))
return false ;
}
// se il lato 0 è di contorno
else if ( nAdjT[0] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back()))
return false ;
}
// se il lato 1 è di contorno
else if ( nAdjT[1] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back()))
return false ;
}
// se il lato 2 è di contorno
else if ( nAdjT[2] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back()))
return false ;
}
// altrimenti non c'è contorno
else {
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::MarchAlongLoop( int nT, int nV, int nTimeStamp, PolyLine& PL) const
{
// mi muovo lungo il loop, un triangolo alla volta
int nCount = 0 ;
bool bEnd = false ;
while ( ! bEnd) {
// altro triangolo
if ( ! MarchOneTria( nT, nV, nTimeStamp, PL, bEnd))
return false ;
// per evitare loop infiniti
++ nCount ;
if ( nCount > 3 * int( m_vTria.size()) + 10)
return true ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::MarchOneTria( int& nT, int& nV, int nTimeStamp,
PolyLine& PL, bool& bEnd) const
{
// verifico esistenza triangolo adiacente, sul lato dopo il vertice
if ( m_vTria[nT].nIdAdjac[nV] == SVT_NULL)
return false ;
int nAdjT = m_vTria[nT].nIdAdjac[nV] ;
// recupero il suo lato di adiacenza
int nAdjS = SVT_NULL ;
for ( int i = 0 ; i < 3 ; ++ i) {
if ( m_vTria[nAdjT].nIdAdjac[i] == nT) {
nAdjS = i ;
break ;
}
}
if ( nAdjS == SVT_NULL)
return false ;
// vertice di fine adiacenza e indice del successivo lato
int nAdjV = Next( nAdjS) ;
// verifico se il lato successivo è un bordo
int nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ;
if ( nNextT == SVT_NULL) {
// se già recuperato
if ( m_vTria[nAdjT].nTemp == nTimeStamp) {
bEnd = true ;
return true ;
}
// dichiaro triangolo analizzato
m_vTria[nAdjT].nTemp = nTimeStamp ;
// aggiungo il lato al loop
nAdjV = Next( nAdjV) ;
PL.AddUPoint( nAdjT, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ;
// verifico anche il successivo
nNextT = m_vTria[nAdjT].nIdAdjac[nAdjV] ;
if ( nNextT == SVT_NULL) {
// aggiungo il lato al loop
nAdjV = Next( nAdjV) ;
PL.AddUPoint( nAdjT, m_vVert[m_vTria[nAdjT].nIdVert[nAdjV]].ptP) ;
}
}
// devo passare al triangolo adiacente
nT = nAdjT ;
nV = nAdjV ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL, bool bAllTria) const
{
// Verifico lo stato
if ( m_nStatus != OK)
return false ;
// Verifico la direzione
Vector3d vtVers = vtDir ;
if ( ! vtVers.Normalize())
return false ;
// Controlli su tolleranza
dTol = max( dTol, 100 * EPS_SMALL) ;
// Determino il riferimento di proiezione
Frame3d frOCS ; frOCS.Set( ORIG, vtVers) ;
Frame3d frBox = frOCS ; frBox.Invert() ;
BBox3d b3Box ;
GetBBox( frBox, b3Box) ;
frOCS.Translate( b3Box.GetMin().z * frOCS.VersZ()) ;
// Ottengo la Silhouette come unione delle regioni dei triangoli proiettati
// calcolo la regione dei triangoli proiettati
PtrOwner<SurfFlatRegion> pSfr ;
Triangle3d Tria ;
int nT = GetFirstTriangle( Tria) ;
while ( nT != SVT_NULL) {
// verifico la normale e il triangolo proiettato
if ( ( ( bAllTria && abs( Tria.GetN() * vtVers) > EPS_ZERO) ||
( ! bAllTria && Tria.GetN() * vtVers > EPS_ZERO)) &&
Tria.Scale( frOCS, 1, 1, 0) && Tria.GetSqMinHeight() > SQ_EPS_SMALL) {
PtrOwner<SurfFlatRegion> pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromTriangle( Tria))) ;
if ( ! IsNull( pSfrTria)) {
if ( bAllTria && Tria.GetN() * vtVers < 0)
pSfrTria->Invert() ;
pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ;
if ( IsNull( pSfr))
pSfr.Set( pSfrTria) ;
else
pSfr->Add( *pSfrTria) ;
}
}
// passo al successivo
nT = GetNextTriangle( nT, Tria) ;
}
// Se non esiste la regione
if ( IsNull( pSfr))
return false ;
// Effettuo contro-offset
pSfr->Offset( -dTol, ICurve::OFF_EXTEND) ;
// Recupero i contorni della regione
for ( int i = 0 ; i < pSfr->GetChunkCount() ; ++ i) {
for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; ++ j) {
PolyLine PL ;
if ( pSfr->ApproxLoopWithLines( i, j, LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL))
vPL.emplace_back( PL) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL, bool bAllTria) const
{
// Verifico lo stato
if ( m_nStatus != OK)
return false ;
// Verifico la direzione
Vector3d vtVers = plPlane.GetVersN() ;
if ( ! vtVers.Normalize())
return false ;
// Controlli su tolleranza
dTol = max( dTol, 100 * EPS_SMALL) ;
// Determino il riferimento di proiezione
Frame3d frOCS ; frOCS.Set( plPlane.GetPoint(), vtVers) ;
// Ottengo la Silhouette come unione delle regioni dei triangoli proiettati (solo parti sopra il piano)
// calcolo la regione dei triangoli proiettati
PtrOwner<SurfFlatRegion> pSfr ;
Triangle3d Tria ;
int nT = GetFirstTriangle( Tria) ;
while ( nT != SVT_NULL) {
// verifico la normale
if ( ( bAllTria && abs( Tria.GetN() * vtVers) > EPS_ZERO) ||
( ! bAllTria && Tria.GetN() * vtVers > EPS_ZERO)) {
// ricavo il poligono equivalente al triangolo
Polygon3d pgTria ;
pgTria.FromTriangle( Tria) ;
// taglio il poligono con il piano
pgTria.Trim( plPlane, false, true, bAllTria) ;
// se rimasto qualcosa
if ( pgTria.GetSideCount() > 0) {
// lo proietto sul piano e creo la regione
if ( pgTria.Scale( frOCS, 1, 1, 0)) {
PtrOwner<SurfFlatRegion> pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLine( pgTria.GetPolyLine()))) ;
if ( ! IsNull( pSfrTria)) {
if ( bAllTria && Tria.GetN() * vtVers < 0)
pSfrTria->Invert() ;
pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ;
if ( IsNull( pSfr))
pSfr.Set( pSfrTria) ;
else
pSfr->Add( *pSfrTria) ;
}
}
}
}
// passo al successivo
nT = GetNextTriangle( nT, Tria) ;
}
// Se non esiste la regione
if ( IsNull( pSfr))
return true ;
// Effettuo contro-offset
pSfr->Offset( -dTol, ICurve::OFF_EXTEND) ;
// Recupero i contorni della regione
for ( int i = 0 ; i < pSfr->GetChunkCount() ; ++ i) {
for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; ++ j) {
PolyLine PL ;
if ( pSfr->ApproxLoopWithLines( i, j, LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL))
vPL.emplace_back( PL) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
SurfTriMesh*
SurfTriMesh::Clone( void) const
{
// alloco oggetto
SurfTriMesh* pStm = new( nothrow) SurfTriMesh ;
if ( pStm != nullptr) {
if ( ! pStm->CopyFrom( *this)) {
delete pStm ;
return nullptr ;
}
}
return pStm ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CopyFrom( const IGeoObj* pGObjSrc)
{
const SurfTriMesh* pStm = GetBasicSurfTriMesh( pGObjSrc) ;
if ( pStm == nullptr)
return false ;
return CopyFrom( *pStm) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CopyFrom( const SurfTriMesh& stmSrc)
{
if ( &stmSrc == this)
return true ;
if ( ! Init( stmSrc.GetVertexSize(), stmSrc.GetTriangleSize(), stmSrc.GetFacetSize()))
return false ;
m_nStatus = stmSrc.m_nStatus ;
m_dLinTol = stmSrc.m_dLinTol ;
m_dBoundaryAng = stmSrc.m_dBoundaryAng ;
m_dCosBndAng = stmSrc.m_dCosBndAng ;
m_dSmoothAng = stmSrc.m_dSmoothAng ;
m_dCosSmAng = stmSrc.m_dCosSmAng ;
m_bShowEdges = stmSrc.m_bShowEdges ;
m_bOriented = stmSrc.m_bOriented ;
m_bClosed = stmSrc.m_bClosed ;
m_bFaceted = stmSrc.m_bFaceted ;
m_bFacEdged = stmSrc.m_bFacEdged ;
m_vVert = stmSrc.m_vVert ;
m_vTria = stmSrc.m_vTria ;
m_vFacet = stmSrc.m_vFacet ;
m_vFacEdge = stmSrc.m_vFacEdge ;
m_nTimeStamp = stmSrc.m_nTimeStamp ;
m_nTempProp[0] = stmSrc.m_nTempProp[0] ;
m_nTempProp[1] = stmSrc.m_nTempProp[1] ;
m_nMaxTFlag = stmSrc.m_nMaxTFlag ;
m_nShells = stmSrc.m_nShells ;
m_vPart = stmSrc.m_vPart ;
m_dTempParam[0] = stmSrc.m_dTempParam[0] ;
m_dTempParam[1] = stmSrc.m_dTempParam[1] ;
return true ;
}
//----------------------------------------------------------------------------
GeoObjType
SurfTriMesh::GetType( void) const
{
return static_cast<GeoObjType>( GEOOBJ_GETTYPE( SurfTriMesh)) ;
}
//----------------------------------------------------------------------------
const string&
SurfTriMesh::GetTitle( void) const
{
static const string sTitle = "SurfTm" ;
return sTitle ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Dump( string& sOut, bool bMM, const char* szNewLine) const
{
// visualizzazione spigoli
sOut += "ShowEdges=" + ToString( GetShowEdges()) + szNewLine ;
// area
double dArea ;
GetArea( dArea) ;
sOut += "Area=" + ToString( GetAreaInUiUnits( dArea, bMM),1) + szNewLine ;
// altri dati per superficie chiusa
if ( m_bClosed) {
double dVolume ;
GetVolume( dVolume) ;
sOut += "Closed Volume=" + ToString( GetVolumeInUiUnits( dVolume, bMM), 1) + szNewLine ;
}
// segnalo eventuale incongruenza di orientamento
if ( ! m_bOriented)
sOut += string( "Inconsistent Orientation") + szNewLine ;
// segnalo numero di parti e di gusci
int nParts = GetPartCount() ;
int nShells = GetShellCount() ;
sOut += string( "Parts=") + ToString( nParts) + string( " Shells=") + ToString( nShells) + szNewLine ;
// numero di vertici
sOut += "Vert : Nbr=" + ToString( GetVertexCount()) +
" Size=" + ToString( GetVertexSize()) + szNewLine ;
// numero di triangoli
sOut += "Tria : Nbr=" + ToString( GetTriangleCount()) +
" Size=" + ToString( GetTriangleSize()) + szNewLine ;
// numero facce, se calcolate
if ( m_bFaceted)
sOut += "Facet : Nbr=" + ToString( GetFacetCount()) +
" Size=" + ToString( GetFacetSize()) + szNewLine ;
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetNgeId( void) const
{
return GEOOBJ_GETNGEID( SurfTriMesh) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Save( NgeWriter& ngeOut) const
{
// tolleranza lineare di costruzione
if ( ! ngeOut.WriteDouble( m_dLinTol, ";"))
return false ;
// angolo limite per mediare le normali
if ( ! ngeOut.WriteDouble( m_dSmoothAng, ";", true))
return false ;
// flag orientata
if ( ! ngeOut.WriteBool( m_bOriented, ";"))
return false ;
// flag aperta/chiusa
if ( ! ngeOut.WriteBool( m_bClosed, ";"))
return false ;
// numero di vertici
if ( ! ngeOut.WriteInt( GetVertexSize(), ";"))
return false ;
// numero di triangoli
if ( ! ngeOut.WriteInt( GetTriangleSize(), ";"))
return false ;
// numero di facce
if ( ! ngeOut.WriteInt( GetFacetSize(), ";", true))
return false ;
// ciclo sui vertici
for ( int i = 0 ; i < int( m_vVert.size()) ; ++ i) {
if ( ! ngeOut.WriteInt( i, ";") ||
! ngeOut.WritePoint( m_vVert[i].ptP, ";") ||
! ngeOut.WriteInt( m_vVert[i].nIdTria, ";") ||
! ngeOut.WriteInt( m_vVert[i].nFlag, ";", true))
return false ;
}
// ciclo sui triangoli
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
if ( ! ngeOut.WriteInt( i, ";") ||
! ngeOut.WriteInt( m_vTria[i].nIdVert[0], ",") ||
! ngeOut.WriteInt( m_vTria[i].nIdVert[1], ",") ||
! ngeOut.WriteInt( m_vTria[i].nIdVert[2], ";") ||
! ngeOut.WriteInt( m_vTria[i].nIdAdjac[0], ",") ||
! ngeOut.WriteInt( m_vTria[i].nIdAdjac[1], ",") ||
! ngeOut.WriteInt( m_vTria[i].nIdAdjac[2], ";") ||
! ngeOut.WriteVector( m_vTria[i].vtN, ";") ||
! ngeOut.WriteInt( m_vTria[i].nIdFacet, ";") ||
! ngeOut.WriteInt( m_vTria[i].nTFlag, ";") ||
! ngeOut.WriteInt( m_vTria[i].nEFlag, ";", true))
return false ;
}
// ciclo sulle facce
int nNumFacet = int( m_vFacet.size()) ;
for ( int i = 0 ; i < nNumFacet ; ++ i) {
bool bEndL = ( ( i + 1) % 10 == 0) || i == ( nNumFacet - 1) ;
if ( ! ngeOut.WriteInt( i, ",") ||
! ngeOut.WriteInt( m_vFacet[i], ";", bEndL))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::PreSave( GdbGeo& Wrapper) const
{
// salvo il flag di visualizzazione spigoli vivi anche in shading (default false)
if ( m_bShowEdges)
Wrapper.SetInfo( GDB_SI_SHOWEDGES, true) ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::PostSave( GdbGeo& Wrapper) const
{
// elimino eventuali info aggiunte nella PreSave
Wrapper.RemoveInfo( GDB_SI_SHOWEDGES) ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Load( NgeReader& ngeIn)
{
// imposto ricalcolo della grafica, della connessione e di hashgrids3d
m_OGrMgr.Clear() ;
m_nMaxTFlag = 0 ;
m_nShells = -1 ;
m_vPart.clear() ;
ResetHashGrids3d() ;
// leggo la prossima linea ( 2 parametri : dLinTol e dSmoothAng)
// tolleranza lineare di costruzione
double dLinTol ;
if ( ! ngeIn.ReadDouble( dLinTol, ";"))
return false ;
// angolo limite per mediare le normali
double dSmoothAng ;
if ( ! ngeIn.ReadDouble( dSmoothAng, ";", true))
return false ;
m_dLinTol = dLinTol ;
m_dSmoothAng = dSmoothAng ;
m_dCosSmAng = cos( dSmoothAng * DEGTORAD) ;
// leggo la prossima linea
// prima di versione 1010 3 parametri : flag di chiusa, num vertici, num tria
bool bOriented ;
bool bClosed ;
int nNumVert ;
int nNumTria ;
int nNumFacet ;
if ( ngeIn.GetFileVersion() < NGE_VER_1010) {
bOriented = false ;
if ( ! ngeIn.ReadBool( bClosed, ";"))
return false ;
if ( ! ngeIn.ReadInt( nNumVert, ";"))
return false ;
if ( ! ngeIn.ReadInt( nNumTria, ";", true))
return false ;
nNumFacet = 0 ;
}
// da versione 1010 5 parametri : flag di orientata, flag di chiusa, num vertici, num tria, num facet
else {
if ( ! ngeIn.ReadBool( bOriented, ";"))
return false ;
if ( ! ngeIn.ReadBool( bClosed, ";"))
return false ;
if ( ! ngeIn.ReadInt( nNumVert, ";"))
return false ;
if ( ! ngeIn.ReadInt( nNumTria, ";"))
return false ;
if ( ! ngeIn.ReadInt( nNumFacet, ";", true))
return false ;
}
// preparo la superficie
if ( ! Init( nNumVert, nNumTria, nNumFacet))
return false ;
m_bOriented = bOriented ;
m_bClosed = bClosed ;
m_bFaceted = ( nNumFacet > 0) ;
// lettura dei vertici
int nInd ;
StmVert vV ;
for ( int i = 0 ; i < nNumVert ; ++ i) {
// leggo la prossima linea ( 4 parametri : Indice, Punto, IdTria, Flag)
// la interpreto e imposto il vertice
if ( ! ngeIn.ReadInt( nInd, ";") ||
! ngeIn.ReadPoint( vV.ptP, ";") ||
! ngeIn.ReadInt( vV.nIdTria, ";") ||
! ngeIn.ReadInt( vV.nFlag, ";", true) ||
! SetVertex( nInd, vV))
return false ;
}
// lettura dei triangoli
StmTria tT ;
for ( int i = 0 ; i < nNumTria ; ++ i) {
// leggo la prossima linea ( 6 o 7 parametri : Indice, IdVertici, IdAdiacenze, Normale, [Facet,] TFlag, EFlag)
// la interpreto e imposto il vertice
if ( ! ngeIn.ReadInt( nInd, ";") ||
! ngeIn.ReadInt( tT.nIdVert[0], ",") ||
! ngeIn.ReadInt( tT.nIdVert[1], ",") ||
! ngeIn.ReadInt( tT.nIdVert[2], ";") ||
! ngeIn.ReadInt( tT.nIdAdjac[0], ",") ||
! ngeIn.ReadInt( tT.nIdAdjac[1], ",") ||
! ngeIn.ReadInt( tT.nIdAdjac[2], ";") ||
! ngeIn.ReadVector( tT.vtN, ";") ||
( ngeIn.GetFileVersion() >= NGE_VER_1010 && ! ngeIn.ReadInt( tT.nIdFacet, ";")) ||
! ngeIn.ReadInt( tT.nTFlag, ";") ||
! ngeIn.ReadInt( tT.nEFlag, ";", true) ||
! SetTriangle( nInd, tT))
return false ;
}
// lettura delle facce
int nT ;
for ( int i = 0 ; i < nNumFacet ; ++ i) {
bool bEndL = ( ( i + 1) % 10 == 0) || i == ( nNumFacet - 1) ;
if ( ! ngeIn.ReadInt( nInd, ",") ||
! ngeIn.ReadInt( nT, ";", bEndL) ||
! SetFacet( nInd, nT))
return false ;
}
// eseguo validazione
return Validate() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::PostLoad( GdbGeo& Wrapper)
{
// recupero eventuale flag di visualizzazione spigoli vivi anche in shading
bool bVal ;
if ( Wrapper.GetInfo( GDB_SI_SHOWEDGES, bVal)) {
m_bShowEdges = bVal ;
Wrapper.RemoveInfo( GDB_SI_SHOWEDGES) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Validate( bool bCorrect)
{
if ( m_nStatus == OK)
return true ;
// Verifico che i vertici riferiti dai triangoli esistano
m_nStatus = OK ;
for ( int i = 0 ; i < GetTriangleSize() && m_nStatus == OK ; ++ i) {
// se triangolo non cancellato
if ( m_vTria[i].nIdVert[0] != SVT_DEL) {
for ( int j = 0 ; j < 3 && m_nStatus == OK ; ++ j) {
if ( m_vTria[i].nIdVert[j] < 0 ||
m_vTria[i].nIdVert[j] >= GetVertexSize() ||
m_vVert[ m_vTria[i].nIdVert[j]].nIdTria == SVT_DEL)
m_nStatus = ERR ;
}
// calcolo eventuale normale mancante
if ( m_nStatus == OK && m_vTria[i].vtN.IsSmall()) {
if ( ! CalcTriangleNormal( i) && bCorrect)
RemoveTriangle( i) ;
}
}
}
// Verifico che i triangoli riferiti dai vertici esistano
for ( int i = 0 ; i < GetVertexSize() && m_nStatus == OK ; ++ i) {
// se vertice non cancellato e con riferimento assegnato
if ( m_vVert[i].nIdTria != SVT_DEL && m_vVert[i].nIdTria != SVT_NULL) {
if ( m_vVert[i].nIdTria < SVT_DEL ||
m_vVert[i].nIdTria >= GetTriangleSize() ||
m_vTria[ m_vVert[i].nIdTria].nIdVert[0] == SVT_DEL) {
if ( bCorrect)
m_vVert[i].nIdTria = SVT_NULL ;
else
m_nStatus = ERR ;
}
}
}
// Verifico che i triangoli riferiti dalle facce esistano
for ( int i = 0 ; i < GetFacetSize() && m_nStatus == OK && m_bFaceted ; ++ i) {
// verifico validità triangolo riferito
if ( m_vFacet[i] <= SVT_NULL ||
m_vFacet[i] >= GetTriangleSize() ||
m_vTria[ m_vFacet[i]].nIdVert[0] == SVT_DEL)
m_bFaceted = false ;
}
// invalido calcolo connessione
m_nShells = -1 ;
m_vPart.clear() ;
return ( m_nStatus == OK) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::FindVertexInTria( int nV, int nT, int& nK) const
{
nK = - 1 ;
for ( int k = 0 ; k < 3 ; k ++) {
if ( nT != SVT_NULL && nV == m_vTria[nT].nIdVert[k]) {
nK = k ;
break ;
}
}
return ( nK != -1) ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::NextIndAroundVertex( int nInd, int nSize, bool bCirc) const
{
nInd = nInd + 1 ;
if ( bCirc && nInd >= nSize)
nInd = nInd % nSize ;
return nInd ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::PrevIndAroundVertex( int nInd, int nSize, bool bCirc) const
{
nInd = nInd - 1 ;
if ( bCirc && nInd < 0)
nInd = ( nInd + nSize) % nSize ;
return nInd ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AdjustVertices( void)
{
// sistemo i puntatori dai vertici ai triangoli
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// se triangolo non cancellato
if ( m_vTria[i].nIdVert[0] != SVT_DEL) {
for ( int j = 0 ; j < 3 ; ++ j) {
if ( m_vVert[ m_vTria[i].nIdVert[j]].nIdTria == SVT_NULL ||
m_vVert[ m_vTria[i].nIdVert[j]].nIdTria == SVT_DEL)
m_vVert[ m_vTria[i].nIdVert[j]].nIdTria = i ;
}
}
}
// tutti i vertici che non puntano a triangoli vanno ora considerati cancellati
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria == SVT_NULL)
m_vVert[i].nIdTria = SVT_DEL ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AdjustAdjacencies( bool AdjustVert)
{
// sistemo le relazioni tra triangoli e vertici
if ( AdjustVert) {
if ( ! AdjustVertices())
return false ;
}
// matrice di incidenza vertici-triangoli
INTMATRIX mVertTria( GetVertexSize()) ;
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// se triangolo non cancellato
if ( m_vTria[i].nIdVert[0] != SVT_DEL) {
for ( int j = 0 ; j < 3 ; ++ j) {
int nVert = m_vTria[i].nIdVert[j] ;
mVertTria[nVert].emplace_back( i) ;
}
}
}
// sistemo i puntatori tra triangoli
bool bModif ;
do {
bModif = false ;
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// se triangolo non cancellato
if ( m_vTria[i].nIdVert[0] != SVT_DEL) {
for ( int j = 0 ; j < 3 ; ++ j) {
// se adiacenza non risolta
if ( m_vTria[i].nIdAdjac[j] == SVT_NULL) {
// vertici all'estremo dello half-edge di indice j
int nVi = m_vTria[i].nIdVert[j] ;
int nVf = m_vTria[i].nIdVert[Next(j)] ;
// indice altro vertice in altro triangolo
int k ;
int nN ;
// triangoli con il vertice all'inizio dello half-edge
nN = int( mVertTria[nVi].size()) ;
for ( int l = 0 ; l < nN ; ++ l) {
int nTi = mVertTria[nVi][l] ;
if ( nTi != i) {
// se altro vertice in comune, aggiusto le adiacenze dei due triangoli
if ( FindVertexInTria( nVf, nTi, k)) {
m_vTria[i].nIdAdjac[j] = nTi ;
// se half-hedge con orientazione opposta (come dovrebbe essere)
// il successivo di k deve coincidere con j
if ( m_vTria[nTi].nIdVert[Next(k)] == m_vTria[i].nIdVert[j])
m_vTria[nTi].nIdAdjac[k] = i ;
else
m_vTria[nTi].nIdAdjac[Prev(k)] = i ;
bModif = true ;
continue ;
}
}
}
// triangoli con il vertice alla fine dello half-edge
nN = int( mVertTria[nVf].size()) ;
for ( int l = 0 ; l < nN ; ++ l) {
int nTf = mVertTria[nVf][l] ;
if ( nTf != i) {
// se altro vertice in comune, aggiusto le adiacenze dei due triangoli
if ( FindVertexInTria( nVi, nTf, k)) {
m_vTria[i].nIdAdjac[j] = nTf ;
// se half-hedge con orientazione opposta (come dovrebbe essere)
// il precedente di k deve coincidere con il successivo j
if ( m_vTria[nTf].nIdVert[Prev(k)] == m_vTria[i].nIdVert[Next(j)])
m_vTria[nTf].nIdAdjac[Prev(k)] = i ;
else
m_vTria[nTf].nIdAdjac[k] = i ;
bModif = true ;
continue ;
}
}
}
}
}
}
}
} while ( bModif) ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AdjustOrientations( void)
{
// se non ci sono almeno 2 triangoli è inutile fare test
if ( m_vTria.size() < 2) {
m_bOriented = true ;
return true ;
}
// incremento time stamp di due step (serve un marcatore intermedio)
m_nTimeStamp += 2 ;
// ciclo sui triangoli
TRINTDEQUE S3iQ ;
bool bOk = true ;
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// se triangolo non cancellato e senza time stamp
if ( m_vTria[i].nIdVert[0] != SVT_DEL &&
abs( m_vTria[i].nTemp) != m_nTimeStamp) {
S3iQ.emplace_back( i, SVT_NULL, 0) ;
while ( ! S3iQ.empty()) {
if ( ! AdjustTriaOrientation( S3iQ))
bOk = false ;
}
}
}
// salvo il risultato
m_bOriented = bOk ;
// in caso di errore ripristino l'orientamento originale
if ( ! m_bOriented){
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
if ( - m_vTria[i].nTemp == m_nTimeStamp)
InvertTriangle( i) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AdjustTriaOrientation( TRINTDEQUE& S3iQ)
{
// recupero e tolgo i dati dalla testa della coda
int nT = S3iQ.front().nI1 ;
int nRefT = S3iQ.front().nI2 ;
int nRefE = S3iQ.front().nI3 ;
S3iQ.pop_front() ;
// assegno time stamp al triangolo
m_vTria[nT].nTemp = m_nTimeStamp ;
// se c'è triangolo di riferimento, devo verificare se da invertire
if ( nRefT != SVT_NULL) {
// cerco indice half-edge in comune
int nE = 0 ;
if ( m_vTria[nT].nIdAdjac[1] == nRefT)
nE = 1 ;
else if ( m_vTria[nT].nIdAdjac[2] == nRefT)
nE = 2 ;
// verifico corrispondenza vertici ( i due inizi devono essere diversi)
if ( m_vTria[nT].nIdVert[nE] == m_vTria[nRefT].nIdVert[nRefE]) {
m_vTria[nT].nTemp = - m_vTria[nT].nTemp ;
InvertTriangle( nT) ;
}
}
// verifico i triangoli adiacenti
bool bOk = true ;
for ( int j = 0 ; j < 3 ; ++ j) {
int nAdjT = m_vTria[nT].nIdAdjac[j] ;
// se non c'è adiacenza o va sul triangolo di provenienza
if ( nAdjT == SVT_NULL || nAdjT == nRefT)
;
// la verifico
else {
// se triangolo adiacente senza timestamp finale e intermedio
if ( abs( m_vTria[nAdjT].nTemp) != m_nTimeStamp &&
m_vTria[nAdjT].nTemp != ( m_nTimeStamp - 1)) {
m_vTria[nAdjT].nTemp = m_nTimeStamp - 1 ;
S3iQ.emplace_back( nAdjT, nT, j) ;
}
// altrimenti verifico che l'orientamento sia congruente
else {
// indice altro half-hedge
int nE = 0 ;
if ( m_vTria[nAdjT].nIdAdjac[1] == nT)
nE = 1 ;
else if ( m_vTria[nAdjT].nIdAdjac[2] == nT)
nE = 2 ;
// verifico corrispondenza vertici ( i due inizi devono essere diversi)
if ( m_vTria[nT].nIdVert[j] == m_vTria[nAdjT].nIdVert[nE])
bOk = false ;
}
}
}
return bOk ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::TestSealing( void)
{
// ciclo sui triangoli
bool bClosed = true ;
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// se triangolo non cancellato
if ( m_vTria[i].nIdVert[0] != SVT_DEL) {
// verifico le adiacenze
if ( m_vTria[i].nIdAdjac[0] == SVT_NULL ||
m_vTria[i].nIdAdjac[1] == SVT_NULL ||
m_vTria[i].nIdAdjac[2] == SVT_NULL) {
bClosed = false ;
break ;
}
}
}
// aggiorno la chiusura della superficie
m_bClosed = bClosed ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AdjustTopology( void)
{
// se non è rimasto alcunché di valido, pulisco tutto ed esco
if ( GetVertexCount() < 3 || GetTriangleCount() < 1) {
Clear() ;
m_bOriented = true ;
m_bClosed = true ;
return true ;
}
// dichiaro sfaccettatura e relativi bordi da ricalcolare
m_bFaceted = false ;
m_bFacEdged = false ;
// invalido calcolo connessione
m_nShells = - 1 ;
m_vPart.clear() ;
// verifica indici
if ( ! Validate( true))
return false ;
// verifica adiacenze
if ( ! AdjustAdjacencies())
return false ;
// verifica continuità orientazione
if ( ! AdjustOrientations())
return false ;
// verifica chiusura
if ( ! TestSealing())
return false ;
// verifica sfaccettatura
if ( ! VerifyFaceting())
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::PackVertices( void)
{
// compatto, sovrascrivendo eventuali vertici cancellati
INTVECTOR vVId ;
vVId.reserve( GetVertexSize()) ;
int nFirstFree = SVT_NULL ;
for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) {
if ( m_vVert[nId].nIdTria != SVT_DEL) {
if ( nFirstFree != SVT_NULL) {
vVId.push_back( nFirstFree) ;
m_vVert[nFirstFree] = m_vVert[nId] ;
m_vVert[nId].nIdTria = SVT_DEL ;
++ nFirstFree ;
}
else
vVId.push_back( nId) ;
}
else {
if ( nFirstFree == SVT_NULL)
nFirstFree = nId ;
vVId.push_back( SVT_DEL) ;
}
}
// se non c'è stata compattazione, esco
if ( nFirstFree == SVT_NULL)
return true ;
// lunghezza vettore indici vertici
int nVIdSize = int( vVId.size()) ;
// aggiorno gli indici ai vertici dai triangoli
for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) {
// recupero gli indici dei vertici del triangolo
int vOId[3] ;
vOId[0] = m_vTria[nId].nIdVert[0] ;
vOId[1] = m_vTria[nId].nIdVert[1] ;
vOId[2] = m_vTria[nId].nIdVert[2] ;
// salto i triangoli cancellati
if ( vOId[0] == SVT_DEL)
continue ;
// verifico la validità degli indici
if ( vOId[0] < 0 || vOId[0] >= nVIdSize ||
vOId[1] < 0 || vOId[1] >= nVIdSize ||
vOId[2] < 0 || vOId[2] >= nVIdSize)
return false ;
// aggiorno il triangolo
m_vTria[nId].nIdVert[0] = vVId[vOId[0]] ;
m_vTria[nId].nIdVert[1] = vVId[vOId[1]] ;
m_vTria[nId].nIdVert[2] = vVId[vOId[2]] ;
}
// ridimensiono l'array dei vertici
m_vVert.resize( nFirstFree) ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::PackTriangles( void)
{
// compatto, sovrascrivendo eventuali triangoli cancellati
INTVECTOR vTId ;
vTId.reserve( GetTriangleSize()) ;
int nFirstFree = SVT_NULL ;
for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) {
if ( m_vTria[nId].nIdVert[0] != SVT_DEL) {
if ( nFirstFree != SVT_NULL) {
vTId.push_back( nFirstFree) ;
m_vTria[nFirstFree] = m_vTria[nId] ;
m_vTria[nId].nIdVert[0] = SVT_DEL ;
++ nFirstFree ;
}
else
vTId.push_back( nId) ;
}
else {
if ( nFirstFree == SVT_NULL)
nFirstFree = nId ;
vTId.push_back( SVT_DEL) ;
}
}
// se non c'è stata compattazione, esco
if ( nFirstFree == SVT_NULL)
return true ;
// Invalido HashGrid
ResetHashGrids3d() ;
// lunghezza vettore indici triangoli
int nTIdSize = int( vTId.size()) ;
// aggiorno gli indici ai triangoli dai vertici
for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) {
// recupero indice dal vertice a triangolo
int nOId = m_vVert[nId].nIdTria ;
// salto vertice cancellato
if ( nOId == SVT_DEL)
continue ;
// verifico la validità dell'indice
if ( nOId < 0 || nOId >= nTIdSize)
return false ;
// aggiorno
m_vVert[nId].nIdTria = vTId[nOId] ;
}
// aggiorno gli indici ai triangoli dai triangoli (adiacenze)
for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) {
// salto i triangoli cancellati
if ( m_vTria[nId].nIdVert[0] == SVT_DEL)
continue ;
// recupero gli indici dei triangoli adiacenti
int vOId[3] ;
vOId[0] = m_vTria[nId].nIdAdjac[0] ;
vOId[1] = m_vTria[nId].nIdAdjac[1] ;
vOId[2] = m_vTria[nId].nIdAdjac[2] ;
// aggiorno opportunamente ogni indice
for ( int j = 0 ; j < 3 ; ++ j) {
if ( vOId[j] == SVT_NULL)
continue ;
if ( vOId[j] < 0 || vOId[j] >= nTIdSize)
return false ;
m_vTria[nId].nIdAdjac[j] = ( vTId[vOId[j]] == SVT_DEL ? SVT_NULL : vTId[vOId[j]]) ;
}
}
// aggiorno gli indici ai triangoli dalle facets
for ( int nId = 0 ; m_bFaceted && nId < GetFacetSize() ; ++ nId) {
// salto le facets non valide
if ( m_vFacet[nId] == SVT_DEL)
continue ;
// verifico validità indice a triangolo
if ( m_vFacet[nId] < 0 || m_vFacet[nId] >= nTIdSize)
return false ;
// aggiorno
m_vFacet[nId] = vTId[m_vFacet[nId]] ;
}
// ridimensiono l'array dei triangoli
m_vTria.resize( nFirstFree) ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByFlatContour( const PolyLine& PL)
{
// eseguo la triangolazione, dopo aver verificato che il contorno sia chiuso e piatto
PNTVECTOR vPnt ;
INTVECTOR vTria ;
Triangulate Tri ;
if ( ! Tri.Make( PL, vPnt, vTria))
return false ;
// inizializzo la superficie
int nVert = int( vPnt.size()) ;
int nTria = int( vTria.size()) / 3 ;
if ( ! Init( nVert, nTria))
return false ;
// inserisco i vertici nella superficie
for ( int i = 0 ; i < int( vPnt.size()) ; ++ i) {
if ( AddVertex( vPnt[i]) == SVT_NULL)
return false ;
}
// recupero i triangoli e li inserisco nella superficie
int vV[3] ;
for ( int i = 0 ; i < nTria ; ++i) {
vV[0] = vTria[3*i] ;
vV[1] = vTria[3*i+1] ;
vV[2] = vTria[3*i+2] ;
if ( AddTriangle( vV) == SVT_NULL)
return false ;
}
// sistemo la topologia
return AdjustTopology() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByPolygonWithHoles( const POLYLINEVECTOR& vPL)
{
INTMATRIX vnPLIndMat ;
vnPLIndMat.push_back( { 0}) ;
for ( int i = 1 ; i < int( vPL.size()) ; ++ i)
vnPLIndMat[0].push_back( i) ;
return CreateByRegion( vPL, vnPLIndMat) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByRegion( const POLYLINEVECTOR& vPL, const INTMATRIX& vnPLIndMat)
{
// eseguo la triangolazione, dopo aver verificato che l'insieme di contorni costituisca una regione
PNTVECTOR vPnt ;
INTVECTOR vTria ;
Triangulate Tri ;
if ( ! Tri.MakeAdvanced( vPL, vPnt, vTria, vnPLIndMat))
return false ;
// inizializzo la superficie
int nVert = int( vPnt.size()) ;
int nTria = int( vTria.size()) / 3 ;
if ( ! Init( nVert, nTria))
return false ;
// inserisco i vertici nella superficie
for ( int i = 0 ; i < int( vPnt.size()) ; ++ i) {
if ( AddVertex( vPnt[i]) == SVT_NULL)
return false ;
}
// recupero i triangoli e li inserisco nella superficie
int vV[3] ;
for ( int i = 0 ; i < nTria ; ++i) {
vV[0] = vTria[3*i] ;
vV[1] = vTria[3*i+1] ;
vV[2] = vTria[3*i+2] ;
if ( AddTriangle( vV) == SVT_NULL)
return false ;
}
// compatto e sistemo la topologia
return AdjustTopology() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByExtrusion( const PolyLine& PL, const Vector3d& vtExtr)
{
// il vettore di estrusione deve essere non nullo
if ( vtExtr.IsSmall())
return false ;
// la polilinea deve avere almeno 2 punti
if ( PL.GetPointNbr() < 2)
return false ;
// imposto ricalcolo
m_nStatus = ERR ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// verifico se la polilinea è chiusa
bool bClosed = PL.IsClosed() ;
// costruisco la mesh
int nPointNbr = PL.GetPointNbr() ;
if ( ! Init( 2 * nPointNbr, 2 * nPointNbr))
return false ;
// inserisco il primo vertice della polilinea e il suo estruso
int nV = -1 ;
Point3d ptP ;
if ( ! PL.GetFirstPoint( ptP))
return false ;
if ( AddVertex( ptP) == SVT_NULL || AddVertex( ptP + vtExtr) == SVT_NULL)
return false ;
nV += 2 ;
// ciclo sui punti della polilinea (per inserire vertice e suo estruso + 2 triangoli per ogni punto)
int nIdV[4] ;
while ( PL.GetNextPoint( ptP, bClosed)) {
// aggiungo due nuovi vertici
if ( AddVertex( ptP) == SVT_NULL || AddVertex( ptP + vtExtr) == SVT_NULL)
return false ;
nV += 2 ;
// aggiungo i due triangoli relativi
nIdV[0] = nV - 2 ;
nIdV[1] = nV - 3 ;
nIdV[2] = nV - 1 ;
nIdV[3] = nV ;
if ( ! AddBiTriangle( nIdV))
return false ;
}
// se curva chiusa, aggiungo gli ultimi due triangoli
if ( bClosed) {
// non devo aggiungere i vertici, perchè coincidono con quelli iniziali
// aggiungo i due triangoli relativi
nIdV[0] = nV ;
nIdV[1] = nV - 1 ;
nIdV[2] = 0 ;
nIdV[3] = 1 ;
if ( ! AddBiTriangle( nIdV))
return false ;
}
// sistemo la topologia
m_nStatus = TO_VERIFY ;
return AdjustTopology() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByPointCurve( const Point3d& ptP, const PolyLine& PL)
{
// verifico validità punto/polilinea
bool bClosed = PL.IsClosed() ;
// se chiusa, la polilinea deve avere almeno 3 punti
if ( bClosed) {
if ( PL.GetPointNbr() < 3)
return false ;
}
// se aperta, almeno 2
else {
if ( PL.GetPointNbr() < 2)
return false ;
}
// imposto ricalcolo
m_nStatus = ERR ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// costruisco la mesh
int nVertNbr = 1 + PL.GetPointNbr() ;
int nTriaNbr = nVertNbr - 2 ;
if ( ! Init( nVertNbr, nTriaNbr))
return false ;
int nIdV[3] ;
// vertice comune a tutti i triangoli
if ( ( nIdV[0] = AddVertex( ptP)) == SVT_NULL)
return false ;
// recupero il punto iniziale su curva 2 e lo inserisco come vertice
Point3d ptP2 ;
if ( ! PL.GetFirstPoint( ptP2))
return false ;
if ( ( nIdV[1] = AddVertex( ptP2)) == SVT_NULL)
return false ;
// ciclo sui punti successivi della curva
while ( PL.GetNextPoint( ptP2, bClosed)) {
// recupero il punto e lo inserisco come vertice
if ( ( nIdV[2] = AddVertex( ptP2)) == SVT_NULL)
return false ;
// inserisco il triangolo A2p -> A1p -> A1s
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// aggiorno indice punto precedente su curva
nIdV[1] = nIdV[2] ;
}
// se chiusa aggiungo l'ultimo triangolo (non il vertice perchè è il primo della curva)
if ( bClosed) {
nIdV[2] = 1 ;
// inserisco il triangolo A2p -> A1p -> A1s
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// sistemo la topologia
m_nStatus = TO_VERIFY ;
return AdjustTopology() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByTwoCurves( const PolyLine& PL1, const PolyLine& PL2, int nRuledType)
{
// imposto ricalcolo
m_nStatus = ERR ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// se rigata a minima distanza tra le due curve
if ( nRuledType == RLT_MINDIST) {
// verifico ci siano almeno due punti diversi per curva
if ( ( PL1.IsClosed() && PL1.GetPointNbr() < 3) || PL1.GetPointNbr() < 2)
return false ;
if ( ( PL2.IsClosed() && PL2.GetPointNbr() < 3) || PL2.GetPointNbr() < 2)
return false ;
// flag di curve entrambe chiuse e correzione conseguente a numero totale punti differenti
bool bClosed = PL1.IsClosed() && PL2.IsClosed() ;
// vettori punti con indice del primo punto dell'altra curva a distanza minima
PNTIVECTOR vPnt1, vPnt2 ;
bool bCommonInternalPoints ;
if ( ! AssociatePolyLinesMinDistPoints( PL1, PL2, vPnt1, vPnt2, bCommonInternalPoints))
return false ;
// verifico che non ci siano punti interni in comune
if ( bCommonInternalPoints)
return false ;
int nTotP1 = int( vPnt1.size()) ;
int nTotP2 = int( vPnt2.size()) ;
// Costruisco la mesh
int nVertNbr = nTotP1 + nTotP2 ;
int nTriaNbr = max( nTotP1, nTotP2) + 1 ;
if ( ! Init( nVertNbr, nTriaNbr))
return false ;
// Recupero i punti iniziali sulla curva 1
int nV1p = -1 ; int nP1p = 0 ;
int nV1s = -1 ; int nP1s = 1 ;
bool bNext1 = ( nP1s < nTotP1) ;
if ( ! bNext1)
return false ;
if ( ( nV1p = AddVertex( vPnt1[nP1p].first)) == SVT_NULL)
return false ;
// Recupero i punti iniziali sulla curva 2
int nV2p = -1 ; int nP2p = 0 ;
int nV2s = -1 ; int nP2s = 1 ;
bool bNext2 = ( nP2s < nTotP2) ;
if ( ! bNext2)
return false ;
int nIdV[3] ;
// se i punti iniziali non coincidono, inserisco il vertice iniziale di 2
if ( ! AreSamePointApprox( vPnt1[nP1p].first, vPnt2[nP2p].first)) {
if ( ( nV2p = AddVertex( vPnt2[nP2p].first)) == SVT_NULL)
return false ;
}
// altrimenti, inserisco un triangolo e mi sposto in avanti su entrambe le curve
else {
// inserisco il vertice V1s
if ( ( nV1s = AddVertex( vPnt1[nP1s].first)) == SVT_NULL)
return false ;
// inserisco il vertice V2s
if ( ( nV2s = AddVertex( vPnt2[nP2s].first)) == SVT_NULL)
return false ;
// inserisco il triangolo V1p -> V1s -> V2s
nIdV[0] = nV1p ;
nIdV[1] = nV1s ;
nIdV[2] = nV2s ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// passo al punto successivo su 1
nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ;
bNext1 = ( nP1s < nTotP1) ;
// passo al punto successivo su 2
nV2p = nV2s ; nP2p = nP2s ; ++ nP2s ;
bNext2 = ( nP2s < nTotP2) ;
}
// ciclo sui punti
while ( bNext1 || bNext2) {
// se non c'è V2s oppure c'è nuovo V1s e la diagonale più corta è V2p -> V1s
if ( ! bNext2 || ( bNext1 && ( nP1s == vPnt2[nP2p].second || vPnt1[nP1s].second == nP2p))) {
// inserisco il vertice V1s (se ultimo e curve chiuse, prendo il primo)
if ( nP1s == nTotP1 - 1 && bClosed)
nV1s = 0 ;
else if ( ( nV1s = AddVertex( vPnt1[nP1s].first)) == SVT_NULL)
return false ;
// inserisco il triangolo V2p -> V1p -> V1s
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = nV1s ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// se i punti correnti coincidono passo al successivo anche su 2
if ( bNext2 && AreSamePointApprox( vPnt1[nP1s].first, vPnt2[nP2s].first)) {
nV2p = nV2s ; nP2p = nP2s ; ++ nP2s ;
bNext2 = ( nP2s < nTotP2) ;
}
// passo al punto successivo su 1
nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ;
bNext1 = ( nP1s < nTotP1) ;
}
// altrimenti è V1p -> V2s
else {
// inserisco il vertice V2s (se ultimo e curve chiuse, prendo il primo)
if ( nP2s == nTotP2 - 1 && bClosed)
nV2s = 1 ;
else if ( ( nV2s = AddVertex( vPnt2[nP2s].first)) == SVT_NULL)
return false ;
// inserisco il triangolo V2p -> V1p -> V2s
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = nV2s ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// se i punti correnti coincidono passo al successivo anche su 1
if ( bNext1 && AreSamePointApprox( vPnt1[nP1s].first, vPnt2[nP2s].first)) {
nV1p = nV1s ; nP1p = nP1s ; ++ nP1s ;
bNext1 = ( nP1s < nTotP1) ;
}
// passo al punto successivo su 2
nV2p = nV2s ; nP2p = nP2s ; ++ nP2s ;
bNext2 = ( nP2s < nTotP2) ;
}
}
}
// altrimenti rigata con parametrizzazione sincrona sulle due curve
else {
// verifico validità polilinee (devono avere almeno 2 punti e non coincidere se non agli estremi aperti)
if ( ! VerifyPolylinesForTwoCurves( PL1, PL2))
return false ;
// flag di curve chiuse
bool bClosed = PL1.IsClosed() && PL2.IsClosed() ;
// recupero i parametri delle due polilinee
double dU1F ; PL1.GetFirstU( dU1F) ;
double dU1L ; PL1.GetLastU( dU1L) ;
double dDeltaU1 = dU1L - dU1F ;
if ( dDeltaU1 < EPS_SMALL)
return false ;
double dU2F ; PL2.GetFirstU( dU2F) ;
double dU2L ; PL2.GetLastU( dU2L) ;
double dDeltaU2 = dU2L - dU2F ;
if ( dDeltaU2 < EPS_SMALL)
return false ;
// costruisco la mesh
bool bTwist = false ;
int nVertNbr = PL1.GetPointNbr() + PL2.GetPointNbr() ;
int nTriaNbr = max( PL1.GetPointNbr(), PL2.GetPointNbr()) + 1 ;
if ( ! Init( nVertNbr, nTriaNbr))
return false ;
// recupero i punti iniziali su curva 1
int nV1p = SVT_NULL ; double dU1p = 0 ; Point3d ptP1p ;
int nV1s = SVT_NULL ; double dU1s = 0 ; Point3d ptP1s ;
bool bNext1 = PL1.GetFirstUPoint( &dU1p, &ptP1p) && PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ;
if ( ! bNext1)
return false ;
double dA1p = 0 ;
double dA1s = ( dU1s - dU1F) / dDeltaU1 ;
if ( ( nV1p = AddVertex( ptP1p)) == SVT_NULL)
return false ;
// recupero i punti iniziali su curva 2
int nV2p = SVT_NULL ; double dU2p = 0 ; Point3d ptP2p ;
int nV2s = SVT_NULL ; double dU2s = 0 ; Point3d ptP2s ;
bool bNext2 = PL2.GetFirstUPoint( &dU2p, &ptP2p) && PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ;
if ( ! bNext2)
return false ;
double dA2p = 0 ;
double dA2s = ( dU2s - dU2F) / dDeltaU2 ;
int nIdV[3] ;
// se i punti iniziali non coincidono, inserisco il vertice iniziale di 2
if ( ! AreSamePointApprox( ptP1p, ptP2p)) {
if ( ( nV2p = AddVertex( ptP2p)) == SVT_NULL)
return false ;
}
// altrimenti, inserisco un triangolo e mi sposto in avanti su entrambe le curve
else {
// inserisco il vertice A1s
if ( ( nV1s = AddVertex( ptP1s)) == SVT_NULL)
return false ;
// inserisco il vertice A2s
if ( ( nV2s = AddVertex( ptP2s)) == SVT_NULL)
return false ;
// inserisco il triangolo A1p -> A1s -> A2s
nIdV[0] = nV1p ;
nIdV[1] = nV1s ;
nIdV[2] = nV2s ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// passo al punto successivo su 1
nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ;
bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ;
if ( bNext1)
dA1s = ( dU1s - dU1F) / dDeltaU1 ;
// passo al punto successivo su 2
nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ;
bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ;
if ( bNext2)
dA2s = ( dU2s - dU2F) / dDeltaU2 ;
}
// ciclo sui punti
while ( bNext1 || bNext2) {
// se richiesto smoothing, ci sono entrambi i successivi, hanno circa lo stesso parametro e i segmenti sono sghembi oltre il limite
double dDiagDist = 0 ;
if ( nRuledType == RLT_ISOPAR_SMOOTH &&
bNext1 && bNext2 && abs( dA1p + dA1s - dA2p - dA2s) < min( dA1s - dA1p, dA2s - dA2p) &&
DistLineLine( ptP1p, ptP2s, ptP2p, ptP1s).GetDist( dDiagDist) && dDiagDist > STM_TWIST_DIAG_DIST) {
bTwist = true ;
// inserisco il vertice A1s
if ( ( nV1s = AddVertex( ptP1s)) == SVT_NULL)
return false ;
// inserisco il vertice A2s
if ( ( nV2s = AddVertex( ptP2s)) == SVT_NULL)
return false ;
// inserisco un nuovo vertice punto medio della linea tra i punti medi
Point3d ptCen = ( ptP1s + ptP1p + ptP2s + ptP2p) / 4 ;
int nVCen = AddVertex( ptCen) ;
if ( nVCen == SVT_NULL)
return false ;
// creo 4 triangoli dai lati del quadrilatero al vertice
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = nVCen ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
nIdV[0] = nV1p ;
nIdV[1] = nV1s ;
nIdV[2] = nVCen ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
nIdV[0] = nV1s ;
nIdV[1] = nV2s ;
nIdV[2] = nVCen ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
nIdV[0] = nV2s ;
nIdV[1] = nV2p ;
nIdV[2] = nVCen ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// passo al punto successivo su 1
nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ;
bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ;
if ( bNext1)
dA1s = ( dU1s - dU1F) / dDeltaU1 ;
// passo al punto successivo su 2
nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ;
bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ;
if ( bNext2)
dA2s = ( dU2s - dU2F) / dDeltaU2 ;
}
// se non c'è dA2s oppure c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s
else if ( ! bNext2 || ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM)) {
// inserisco il vertice A1s
if ( ( nV1s = AddVertex( ptP1s)) == SVT_NULL)
return false ;
// inserisco il triangolo A2p -> A1p -> A1s
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = nV1s ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// se i punti correnti coincidono passo al successivo anche su 2
if ( AreSamePointApprox( ptP1s, ptP2s)) {
nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ;
bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ;
if ( bNext2)
dA2s = ( dU2s - dU2F) / dDeltaU2 ;
}
// passo al punto successivo su 1
nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ;
bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ;
if ( bNext1)
dA1s = ( dU1s - dU1F) / dDeltaU1 ;
}
// altrimenti è dA1p -> dA2s
else {
// inserisco il vertice A2s
if ( ( nV2s = AddVertex( ptP2s)) == SVT_NULL)
return false ;
// inserisco il triangolo A2p -> A1p -> A2s
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = nV2s ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// se i punti correnti coincidono passo al successivo anche su 1
if ( AreSamePointApprox( ptP1s, ptP2s)) {
nV1p = nV1s ; dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ;
bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, bClosed) ;
if ( bNext1)
dA1s = ( dU1s - dU1F) / dDeltaU1 ;
}
// passo al punto successivo su 2
nV2p = nV2s ; dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ;
bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, bClosed) ;
if ( bNext2)
dA2s = ( dU2s - dU2F) / dDeltaU2 ;
}
}
// se curve chiuse, aggiungo gli ultimi due triangoli
if ( bClosed) {
dA1s = 1 ;
dA2s = 1 ;
// se la diagonale più corta è dA2p -> dA1s = 0
if ( ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) {
// inserisco il triangolo A2p -> A1p -> A1s = 0
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = 0 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// inserisco il triangolo A2p -> A1s = 0 -> A2s = 1
nIdV[0] = nV2p ;
nIdV[1] = 0 ;
nIdV[2] = 1 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// altrimenti è dA1p -> dA2s = 1
else {
// inserisco il triangolo A2p -> A1p -> A2s = 1
nIdV[0] = nV2p ;
nIdV[1] = nV1p ;
nIdV[2] = 1 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// inserisco il triangolo A1p -> A1s = 0 -> A2s = 1
nIdV[0] = nV1p ;
nIdV[1] = 0 ;
nIdV[2] = 1 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
}
// in presenza di twist aumento il limite sulla deviazione angolare
if ( bTwist)
SetSmoothAngle( STM_TWIST_SMOOTH_ANG) ;
}
// sistemo la topologia
m_nStatus = TO_VERIFY ;
return AdjustTopology() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::VerifyPolylinesForTwoCurves( const PolyLine& PL1, const PolyLine& PL2) const
{
// recupero se chiusa
bool bClosed = PL1.IsClosed() && PL2.IsClosed() ;
// se chiuse, devono avere almeno 3 punti
if ( bClosed) {
if ( PL1.GetPointNbr() < 3 || PL2.GetPointNbr() < 3)
return false ;
}
// se aperte, almeno 2
else {
if ( PL1.GetPointNbr() < 2 || PL2.GetPointNbr() < 2)
return false ;
}
// verifico che non ci siano punti interni in comune a pari parametro
// recupero i parametri delle due polilinee
double dU1F ; PL1.GetFirstU( dU1F) ;
double dU1L ; PL1.GetLastU( dU1L) ;
double dDeltaU1 = dU1L - dU1F ;
if ( dDeltaU1 < EPS_SMALL)
return false ;
double dU2F ; PL2.GetFirstU( dU2F) ;
double dU2L ; PL2.GetLastU( dU2L) ;
double dDeltaU2 = dU2L - dU2F ;
if ( dDeltaU2 < EPS_SMALL)
return false ;
// ciclo sui punti
double dU1p = 0 ; Point3d ptP1p ;
double dU1s = 0 ; Point3d ptP1s ;
bool bNext1 = PL1.GetFirstUPoint( &dU1p, &ptP1p) && PL1.GetNextUPoint( &dU1s, &ptP1s, true) ;
if ( ! bNext1)
return true ;
double dA1p = 0 ;
double dA1s = ( dU1s - dU1F) / dDeltaU1 ;
double dU2s = 0 ; Point3d ptP2s ;
double dU2p = 0 ; Point3d ptP2p ;
bool bNext2 = PL2.GetFirstUPoint( &dU2p, &ptP2p) && PL2.GetNextUPoint( &dU2s, &ptP2s, true) ;
if ( ! bNext2)
return true ;
double dA2p = 0 ;
double dA2s = ( dU2s - dU2F) / dDeltaU2 ;
// se chiuse, verifico la coincidenza dell'inizio delle due curve
if ( bClosed && AreSamePointApprox( ptP1p, ptP2p))
return false ;
// verifiche sui punti successivi (non sugli ultimi)
while ( bNext1 || bNext2) {
// se c'è nuovo dA1s e la diagonale più corta è dA2p -> dA1s oppure non c'è dA2s
if ( ( bNext1 && ( dA1s - dA2p) <= ( dA2s - dA1p) + EPS_PARAM) || ! bNext2) {
// verifico se coincidono
if ( AreSamePointApprox( ptP2p, ptP1s))
return false ;
// passo al punto successivo su 1
dA1p = dA1s ; dU1p = dU1s ; ptP1p = ptP1s ;
bNext1 = PL1.GetNextUPoint( &dU1s, &ptP1s, true) ;
if ( bNext1)
dA1s = ( dU1s - dU1F) / dDeltaU1 ;
}
// altrimenti è dA1p -> dA2s = 1
else {
// verifico se coincidono
if ( AreSamePointApprox( ptP1p, ptP2s))
return false ;
// passo al punto successivo su 2
dA2p = dA2s ; dU2p = dU2s ; ptP2p = ptP2s ;
bNext2 = PL2.GetNextUPoint( &dU2s, &ptP2s, true) ;
if ( bNext2)
dA2s = ( dU2s - dU2F) / dDeltaU2 ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByRevolution( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx,
double dAngRot, double dStepRot)
{
return CreateByScrewing( PL, ptAx, vtAx, dAngRot, dStepRot, 0) ;
}
//----------------------------------------------------------------------------
static bool
VeryfyPolylineForRevolution( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx)
{
// calcolo un riferimento con origine ptAx e asseZ vtAx
Frame3d frAx ;
if ( ! frAx.Set( ptAx, vtAx))
return false ;
// numero di segmenti della polilinea
int nTotSeg = PL.GetLineNbr() ;
// flag di chiusa
bool bClosed = PL.IsClosed() ;
// recupero il primo punto e lo porto nel riferimento Ax e quindi lo proietto su XY
Point3d ptPp ;
if ( ! PL.GetFirstPoint( ptPp))
return false ;
ptPp.ToLoc( frAx) ;
ptPp.z = 0 ;
// calcolo la distanza tra i segmenti della polilinea portati nel piano XY del rif Ax e l'origine
int nSeg = 0 ;
Point3d ptPc ;
while ( PL.GetNextPoint( ptPc)) {
// punto corrente
ptPc.ToLoc( frAx) ;
ptPc.z = 0 ;
++ nSeg ;
// verifico distanza
// se la curva è chiusa oppure è vicina all'asse ( entro EPS_SMALL), ma non ho che sono start o end ad essere sull'asse
// allora dovrò rimaneggiare la polyline
if ( DistPointLine( ORIG, ptPp, ptPc).IsSmall()) {
if ( bClosed ||
( ! ( nSeg == 1 && AreSamePointApprox( ORIG, ptPp)) &&
! ( nSeg == nTotSeg && AreSamePointApprox( ORIG, ptPc))))
return false ;
}
// salvo punto
ptPp = ptPc ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustPolylineForRevolution( PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx)
{
Vector3d vtAxN = vtAx ;
vtAxN.Normalize() ;
// determino il piano della polilinea
int nRank ;
Point3d ptCen ;
Vector3d vtScN ;
if ( ! PL.IsFlat( nRank, ptCen, vtScN, 10 * EPS_SMALL) || nRank == 0)
return false ;
Plane3d plSect ;
if ( nRank >= 2)
plSect.Set( ptCen, vtScN) ;
else {
if ( ! plSect.Set( ptCen, vtAxN ^ vtScN))
return false ;
}
// verifico che l'asse giaccia nel piano
if ( ! PointInPlaneApprox( ptAx, plSect) || ! PointInPlaneApprox( ptAx + 100 * vtAxN, plSect))
return false ;
// calcolo il piano perpendicolare a quello della curva e contenente l'asse
Vector3d vtTrN = ( ptCen - ptAx) - ( ptCen - ptAx) * vtAxN * vtAxN ;
if ( ! vtTrN.Normalize()) {
vtTrN = vtAxN ^ vtScN ;
vtTrN.Normalize() ;
}
Plane3d plTrim ;
plTrim.Set( ptAx, vtTrN) ;
// Trimmo la polilinea con il piano (leggermente spostato)
const double DIST_SIC = 5 * EPS_SMALL ;
plTrim.Translate( DIST_SIC * vtTrN) ;
PL.Trim( plTrim, false) ;
// Se polilinea risultante è aperta con estremità molto vicine all'asse le porto su questo
if ( ! PL.IsClosed()) {
// verifico l'inizio
double dUStart ;
Point3d ptStart ;
PL.GetFirstUPoint( &dUStart, &ptStart) ;
DistPointLine dStAx( ptStart, ptAx, vtAx, 1, false) ;
double dStDist ;
if ( dStAx.GetDist( dStDist) && dStDist < 1.1 * DIST_SIC) {
dStAx.GetMinDistPoint( ptStart) ;
PL.EraseFirstUPoint() ;
PL.AddUPoint( dUStart, ptStart, false) ;
}
// verifico la fine
double dUEnd ;
Point3d ptEnd ;
PL.GetLastUPoint( &dUEnd, &ptEnd) ;
DistPointLine dEnAx( ptEnd, ptAx, vtAx, 1, false) ;
double dEnDist ;
if ( dEnAx.GetDist( dEnDist) && dEnDist < 1.1 * DIST_SIC) {
dEnAx.GetMinDistPoint( ptEnd) ;
PL.EraseLastUPoint() ;
PL.AddUPoint( dUEnd, ptEnd, true) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CreateByScrewing( const PolyLine& PL, const Point3d& ptAx, const Vector3d& vtAx,
double dAngRot, double dStepRot, double dMove)
{
// verifico che l'asse di rotazione sia non nullo
if ( vtAx.IsSmall())
return false ;
// verifico se solo rivoluzione
bool bOnlyRev = ( abs( dMove) < EPS_SMALL) ;
// verifico che l'angolo di rotazione sia significativo e, se solo rivoluzione, non superi un giro
if ( abs( dAngRot) < EPS_ANG_SMALL)
return false ;
if ( bOnlyRev && abs( dAngRot) > ANG_FULL)
dAngRot = _copysign( ANG_FULL, dAngRot) ;
// verifico se rotazione completa
bool bFullRev = bOnlyRev && ( abs( abs( dAngRot) - ANG_FULL) < EPS_ANG_SMALL) ;
// aggiusto il valore dell'angolo di step
const double MIN_STEP_ROT = 1 ;
const double MAX_STEP_ROT = 90 ;
if ( abs( dStepRot) < MIN_STEP_ROT)
dStepRot = _copysign( MIN_STEP_ROT, dAngRot) ;
else if ( abs( dStepRot) > MAX_STEP_ROT)
dStepRot = _copysign( MAX_STEP_ROT, dAngRot) ;
else
dStepRot = _copysign( dStepRot, dAngRot) ;
// calcolo il numero di step
int nStep = int( dAngRot / dStepRot) ;
nStep = max( nStep, 1) ;
double dCosStepRot = cos( dAngRot / nStep * DEGTORAD) ;
double dSinStepRot = sin( dAngRot / nStep * DEGTORAD) ;
Vector3d vtStepMove = vtAx ;
vtStepMove.Normalize() ;
vtStepMove *= ( dMove / nStep) ;
if ( bFullRev)
-- nStep ;
// verifico che la polilinea non attraversi l'asse di rivoluzione o lo tocchi in punti interni
PolyLine MyPL = PL ;
if ( ! VeryfyPolylineForRevolution( MyPL, ptAx, vtAx) && ! AdjustPolylineForRevolution( MyPL, ptAx, vtAx)) {
LOG_ERROR( GetEGkLogger(), "StmCreateByRevolution : polyline inside meets axis")
return false ;
}
// imposto ricalcolo
m_nStatus = ERR ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// verifico se la polilinea è chiusa
bool bClosed = MyPL.IsClosed() ;
// costruisco la mesh
int nPointNbr = MyPL.GetPointNbr() ;
if ( ! Init( ( 1 + nStep) * nPointNbr, ( 1 + nStep) * nPointNbr))
return false ;
// inserisco il primo punto della polilinea e i suoi ruotati
int nV = -1 ;
Point3d ptP ;
// recupero il punto
if ( ! MyPL.GetFirstPoint( ptP))
return false ;
// inserisco il primo vertice
if ( AddVertex( ptP) == SVT_NULL)
return false ;
++ nV ;
// verifico se il punto giace sull'asse e vi sta fisso
bool bPrevOnAx = bOnlyRev && DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ;
int nVPrevOnAx = nV ;
// se non è fisso sull'asse, inserisco le copie ruotate
if ( ! bPrevOnAx) {
for ( int i = 1 ; i <= nStep ; ++i) {
ptP.Rotate( ptAx, vtAx, dCosStepRot, dSinStepRot) ;
if ( ! bOnlyRev)
ptP.Translate( vtStepMove) ;
if ( AddVertex( ptP) == SVT_NULL)
return false ;
++ nV ;
}
}
// ciclo sui punti della polilinea (per inserire vertice e suoi ruotati + 2 triangoli per ogni punto)
int nIdV[4] ;
while ( MyPL.GetNextPoint( ptP, bClosed)) {
// aggiungo il primo vertice
if ( AddVertex( ptP) == SVT_NULL)
return false ;
++ nV ;
// verifico se il punto giace sull'asse e vi sta fisso
bool bOnAx = bOnlyRev && DistPointLine( ptP, ptAx, vtAx, 1, false).IsSmall() ;
// ciclo sugli step
for ( int i = 1 ; i <= nStep ; ++i) {
// se non è fisso sull'asse, inserisco le copie ruotate
if ( ! bOnAx) {
ptP.Rotate( ptAx, vtAx, dCosStepRot, dSinStepRot) ;
if ( ! bOnlyRev)
ptP.Translate( vtStepMove) ;
if ( AddVertex( ptP) == SVT_NULL)
return false ;
++ nV ;
}
// per i controlli già fatti non è possibile avere contemp. prec e corr su asse
// se il precedente è sull'asse, aggiungo un solo triangolo
if ( bPrevOnAx) {
nIdV[0] = nVPrevOnAx ;
nIdV[1] = nV ;
nIdV[2] = nV - 1 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// se il corrente è sull'asse, aggiungo un solo triangolo
else if ( bOnAx) {
nIdV[0] = nV - ( nStep + 2) + i ;
nIdV[1] = nIdV[0] + 1 ;
nIdV[2] = nV ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// altrimenti aggiungo due triangoli
else {
nIdV[0] = nV - ( nStep + 2) ;
nIdV[1] = nIdV[0] + 1 ;
nIdV[2] = nV ;
nIdV[3] = nV - 1 ;
if ( ! AddBiTriangle( nIdV))
return false ;
}
}
// se rivoluzione completa, aggiungo i due triangoli di chiusura
if ( bFullRev) {
// per i controlli già fatti non è possibile avere contemp. prec e corr su asse
// se il precedente è sull'asse, aggiungo un solo triangolo
if ( bPrevOnAx) {
nIdV[0] = nVPrevOnAx ;
nIdV[1] = nV - nStep ;
nIdV[2] = nV ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// se il corrente è sull'asse, aggiungo un solo triangolo
else if ( bOnAx) {
nIdV[0] = nV - 1 ;
nIdV[1] = nV - ( nStep + 1) ;
nIdV[2] = nV ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// altrimenti aggiungo due triangoli
else {
nIdV[0] = nV - ( nStep + 1) ;
nIdV[1] = nV - ( 2 * nStep + 1) ;
nIdV[2] = nV - nStep ;
nIdV[3] = nV ;
if ( ! AddBiTriangle( nIdV))
return false ;
}
}
bPrevOnAx = false ;
}
// altrimenti ultimo punto di polilinea chiusa
if ( bClosed) {
for ( int i = 1 ; i <= nStep ; ++i) {
// non devo aggiungere i vertici, perchè coincidono con quelli iniziali
// aggiungo triangolo in basso a sinistra
nIdV[0] = nV - nStep + i - 1 ; nIdV[1] = nV - nStep + i ; nIdV[2] = i ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// aggiungo triangolo in alto a destra
nIdV[0] = nV - nStep + i - 1 ; nIdV[1] = i ; nIdV[2] = i - 1 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// se rivoluzione completa, aggiungo i due triangoli di chiusura
if ( bFullRev) {
// aggiungo triangolo in basso a sinistra
nIdV[0] = nV ; nIdV[1] = nV - nStep ; nIdV[2] = 0 ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// aggiungo triangolo in alto a destra
nIdV[0] = nV ; nIdV[1] = 0 ; nIdV[2] = nStep ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
}
// sistemo la topologia
m_nStatus = TO_VERIFY ;
return AdjustTopology() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AddBiTriangle( const int nIdVert[4])
{
// 0 <- 3
// | |
// 1 -> 2
int nIdV[3] ;
// se la diagonale 0->2 è uguale o più corta della 1->3
if ( SqDist( m_vVert[nIdVert[0]].ptP, m_vVert[nIdVert[2]].ptP) <=
SqDist( m_vVert[nIdVert[1]].ptP, m_vVert[nIdVert[3]].ptP) + EPS_SMALL) {
// triangolo 0->1->2
nIdV[0] = nIdVert[0] ;
nIdV[1] = nIdVert[1] ;
nIdV[2] = nIdVert[2] ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// triangolo 0->2->3
nIdV[0] = nIdVert[0] ;
nIdV[1] = nIdVert[2] ;
nIdV[2] = nIdVert[3] ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
// altrimenti uso la 1->3
else {
// triangolo 0->1->3
nIdV[0] = nIdVert[0] ;
nIdV[1] = nIdVert[1] ;
nIdV[2] = nIdVert[3] ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
// triangolo 1->2->3
nIdV[0] = nIdVert[1] ;
nIdV[1] = nIdVert[2] ;
nIdV[2] = nIdVert[3] ;
if ( AddTriangle( nIdV) == SVT_NULL)
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::DoCompacting( double dTol)
{
// imposto ricalcolo
m_nStatus = ERR ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// definisco un Grid per i vertici della superficie
PointGrid3d VertGrid ;
int nBuckets = GetVertexSize() ;
VertGrid.Init( nBuckets, max( dTol, 100 * EPS_SMALL)) ;
// inserisco i vertici della trimesh (evitando ripetizioni coi precedenti),
// salvando in un vettore di reindirizzo i nuovi Id
INTVECTOR vVId ;
vVId.reserve( GetVertexSize()) ;
for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) {
// salto i vertici cancellati (ma ne occupo il posto nel vettore di reindirizzo)
if ( m_vVert[nId].nIdTria == SVT_DEL) {
vVId.push_back( SVT_DEL) ;
continue ;
}
// recupero la posizione geometrica del vertice
Point3d ptP = m_vVert[nId].ptP ;
// se non c'è già un vertice con la stessa posizione lo inserisco nel grid
int nAliasId ;
if ( ! VertGrid.Find( ptP, dTol, nAliasId)) {
VertGrid.InsertPoint( ptP, nId) ;
// salvo l'Id nel vettore di reindirizzo
vVId.push_back( nId) ;
}
// c'è un vertice coincidente
else {
// salvo l'Id alias nel vettore di reindirizzo
vVId.push_back( nAliasId) ;
// marco il vertice come cancellato
m_vVert[nId].nIdTria = SVT_DEL ;
}
}
int nVIdSize = int( vVId.size()) ;
// sistemo gli indici dei vertici nei triangoli
INTVECTOR vInvalidIds ;
for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) {
// salto i triangoli cancellati
if ( m_vTria[nId].nIdVert[0] == SVT_DEL)
continue ;
// recupero gli indici dei vertici del triangolo
int vOId[3]{ m_vTria[nId].nIdVert[0],
m_vTria[nId].nIdVert[1],
m_vTria[nId].nIdVert[2]} ;
// verifico la validità degli indici
if ( vOId[0] < 0 || vOId[0] >= nVIdSize ||
vOId[1] < 0 || vOId[1] >= nVIdSize ||
vOId[2] < 0 || vOId[2] >= nVIdSize)
return false ;
// aggiorno il triangolo
m_vTria[nId].nIdVert[0] = vVId[vOId[0]] ;
m_vTria[nId].nIdVert[1] = vVId[vOId[1]] ;
m_vTria[nId].nIdVert[2] = vVId[vOId[2]] ;
// verifico se triangolo da rimuovere per vertici coincidenti
bool bRemove = false ;
int nTAdj1 = SVT_NULL, nTAdj2 = SVT_NULL ;
if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[1]) {
nTAdj1 = m_vTria[nId].nIdAdjac[1] ;
nTAdj2 = m_vTria[nId].nIdAdjac[2] ;
bRemove = true ;
}
else if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[2]) {
nTAdj1 = m_vTria[nId].nIdAdjac[0] ;
nTAdj2 = m_vTria[nId].nIdAdjac[1] ;
bRemove = true ;
}
else if ( m_vTria[nId].nIdVert[1] == m_vTria[nId].nIdVert[2]) {
nTAdj1 = m_vTria[nId].nIdAdjac[0] ;
nTAdj2 = m_vTria[nId].nIdAdjac[2] ;
bRemove = true ;
}
if ( bRemove) {
// sistemo le contro adiacenze
if ( nTAdj1 != SVT_NULL) {
for ( int j = 0 ; j < 3 ; ++ j)
if ( m_vTria[nTAdj1].nIdAdjac[j] == nId)
m_vTria[nTAdj1].nIdAdjac[j] = nTAdj2 ;
}
if ( nTAdj2 != SVT_NULL) {
for ( int j = 0 ; j < 3 ; ++ j)
if ( m_vTria[nTAdj2].nIdAdjac[j] == nId)
m_vTria[nTAdj2].nIdAdjac[j] = nTAdj1 ;
}
// rimuovo il triangolo
RemoveTriangle( nId) ;
}
// verifico se il triangolo va rimosso per normale non calcolabile
else if ( ! CalcTriangleNormal( nId))
vInvalidIds.emplace_back( nId) ;
}
// elimino triangoli invalidi ( con gestione speciale per evitare T-junctions)
if ( ! vInvalidIds.empty())
RemoveInvalidTriangles( vInvalidIds) ;
// compatto il vettore dei vertici
if ( ! PackVertices())
return false ;
// ricalcolo le adiacenze
m_nStatus = TO_VERIFY ;
if ( ! AdjustTopology())
return false ;
// compatto il vettore dei triangoli
return PackTriangles() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::DoSewing( const ISurfTriMesh& stmOther, const Frame3d& frOther, double dTol)
{
// recupero l'altra superficie
const SurfTriMesh* pOther = GetBasicSurfTriMesh( &stmOther) ;
if ( pOther == nullptr)
return false ;
// imposto ricalcolo
m_nStatus = ERR ;
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// definisco un Grid per i vertici delle due superfici
PointGrid3d VertGrid ;
int nBuckets = max( GetVertexSize() + pOther->GetVertexSize(), 1000) ;
VertGrid.Init( nBuckets) ;
// inserisco i vertici della trimesh corrente (li considero tutti diversi tra loro)
for ( int nId = 0 ; nId < GetVertexSize() ; ++ nId) {
// salto i vertici cancellati
if ( m_vVert[nId].nIdTria == SVT_DEL)
continue ;
// inserisco il vertice nella griglia
VertGrid.InsertPoint( m_vVert[nId].ptP, nId) ;
}
// inserisco i vertici dell'altra trimesh (evitando ripetizioni coi precedenti),
// salvando in un vettore di reindirizzo i nuovi Id
INTVECTOR vVId ;
vVId.reserve( pOther->GetVertexSize()) ;
for ( int nOId = 0 ; nOId < pOther->GetVertexSize() ; ++ nOId) {
// salto i vertici cancellati (ma ne occupo il posto nel vettore di reindirizzo)
if ( pOther->m_vVert[nOId].nIdTria == SVT_DEL) {
vVId.push_back( SVT_DEL) ;
continue ;
}
// recupero la posizione geometrica del vertice
Point3d ptOP = pOther->m_vVert[nOId].ptP ;
// la porto nel riferimento della prima superficie
ptOP.ToGlob( frOther) ;
// se non c'è già un vertice con la stessa posizione lo inserisco
int nNewId ;
if ( ! VertGrid.Find( ptOP, dTol, nNewId)) {
if ( ( nNewId = AddVertex( ptOP)) == SVT_NULL)
return false ;
VertGrid.InsertPoint( ptOP, nNewId) ;
}
// salvo il nuovo Id nel vettore di reindirizzo
vVId.push_back( nNewId) ;
}
int nVIdSize = int( vVId.size()) ;
// aggiungo i triangoli dell'altra trimesh
for ( int nOtId = 0 ; nOtId < pOther->GetTriangleSize() ; ++ nOtId) {
// recupero gli indici dei vertici del triangolo
int vOId[3] ;
vOId[0] = pOther->m_vTria[nOtId].nIdVert[0] ;
vOId[1] = pOther->m_vTria[nOtId].nIdVert[1] ;
vOId[2] = pOther->m_vTria[nOtId].nIdVert[2] ;
// salto i triangoli cancellati
if ( vOId[0] == SVT_DEL)
continue ;
// verifico la validità degli indici
if ( vOId[0] < 0 || vOId[0] >= nVIdSize ||
vOId[1] < 0 || vOId[1] >= nVIdSize ||
vOId[2] < 0 || vOId[2] >= nVIdSize)
return false ;
int vId[3] ;
vId[0] = vVId[vOId[0]] ;
vId[1] = vVId[vOId[1]] ;
vId[2] = vVId[vOId[2]] ;
// se triangolo ancora valido, lo inserisco
if ( vId[0] != vId[1] && vId[0] != vId[2] && vId[1] != vId[2]) {
if ( AddTriangle( vId) == SVT_NULL)
return false ;
}
}
// compatto il vettore dei vertici
if ( ! PackVertices())
return false ;
// ricalcolo le adiacenze
m_nStatus = TO_VERIFY ;
if ( ! AdjustTopology())
return false ;
// compatto il vettore dei triangoli
return PackTriangles() ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetLocalBBox( BBox3d& b3Loc, int nFlag) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// assegno il box in locale
b3Loc.Reset() ;
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
b3Loc.Add( m_vVert[i].ptP) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// assegno il box nel riferimento
b3Ref.Reset() ;
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL) {
Point3d ptTemp = m_vVert[i].ptP ;
ptTemp.ToGlob( frRef) ;
b3Ref.Add( ptTemp) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Translate( const Vector3d& vtMove)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// traslo i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.Translate( vtMove) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità dell'asse di rotazione
if ( vtAx.IsSmall())
return false ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// ruoto i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.Rotate( ptAx, vtAx, dCosAng, dSinAng) ;
}
// ruoto le normali delle facce
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
if ( m_vTria[i].nIdVert[0] != SVT_DEL)
m_vTria[i].vtN.Rotate( vtAx, dCosAng, dSinAng) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico non sia nulla
if ( abs( dCoeffX) < EPS_ZERO && abs( dCoeffY) < EPS_ZERO && abs( dCoeffZ) < EPS_ZERO)
return false ;
// calcolo bbox allineato con riferimento di scalatura e senza tener conto dello spessore
// lo scalo per verificare se tutto si riduce a un punto o una linea (solo se diretta come assi ref)
BBox3d b3Ref ;
if ( ! GetBBox( frRef, b3Ref))
return false ;
Vector3d vtDelta = b3Ref.GetMax() - b3Ref.GetMin() ;
bool bZeroX = ( abs( vtDelta.x * dCoeffX) < EPS_SMALL) ;
bool bZeroY = ( abs( vtDelta.y * dCoeffY) < EPS_SMALL) ;
bool bZeroZ = ( abs( vtDelta.z * dCoeffZ) < EPS_SMALL) ;
if ( ( bZeroX && bZeroY) || ( bZeroX && bZeroZ) || ( bZeroY && bZeroZ))
return false ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// scalo i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ;
}
// determino se contiene anche un mirror (numero dispari di coefficienti negativi)
bool bMirror = ( dCoeffX < 0) ;
bMirror = ( bMirror ? ( dCoeffY > 0) : ( dCoeffY < 0)) ;
bMirror = ( bMirror ? ( dCoeffZ > 0) : ( dCoeffZ < 0)) ;
// se c'è mirror, devo invertire le facce
if ( bMirror) {
for ( int i = 0 ; i < GetTriangleSize() ; ++ i)
if ( m_vTria[i].nIdVert[0] != SVT_DEL)
InvertTriangle( i) ;
}
// rimuovo i triangoli resi invalidi dalla scalatura
bool bOk = DoCompacting() ;
// rimuovo i triangoli doppi
bool bModified = false ;
bOk = bOk && RemoveDoubleTriangles( bModified) ;
if ( bModified)
bOk = bOk && ( AdjustVertices() && DoCompacting()) ;
return bOk ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::InvertTriangle( int nT)
{
// controllo validità triangolo
if ( ! ExistsTriangle( nT))
return true ;
// scambio di due vertici
swap( m_vTria[nT].nIdVert[1], m_vTria[nT].nIdVert[2]) ;
// scambio delle conseguenti due adiacenze
swap( m_vTria[nT].nIdAdjac[0], m_vTria[nT].nIdAdjac[2]) ;
// inversione della normale
m_vTria[nT].vtN.Invert() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::CalcTriangleNormal( int nT)
{
// controllo validità triangolo
if ( m_vTria[nT].nIdVert[0] == SVT_DEL)
return true ;
// controllo validità vertici riferiti dal triangolo
if ( m_vTria[nT].nIdVert[0] < 0 ||
m_vTria[nT].nIdVert[0] >= GetVertexSize() ||
m_vVert[m_vTria[nT].nIdVert[0]].nIdTria == SVT_DEL ||
m_vTria[nT].nIdVert[1] < 0 ||
m_vTria[nT].nIdVert[1] >= GetVertexSize() ||
m_vVert[m_vTria[nT].nIdVert[1]].nIdTria == SVT_DEL ||
m_vTria[nT].nIdVert[2] < 0 ||
m_vTria[nT].nIdVert[2] >= GetVertexSize() ||
m_vVert[m_vTria[nT].nIdVert[2]].nIdTria == SVT_DEL)
return false ;
// calcolo vettori come due lati consecutivi del triangolo
Vector3d vtV1 = m_vVert[m_vTria[nT].nIdVert[1]].ptP - m_vVert[m_vTria[nT].nIdVert[0]].ptP ;
Vector3d vtV2 = m_vVert[m_vTria[nT].nIdVert[2]].ptP - m_vVert[m_vTria[nT].nIdVert[1]].ptP ;
Vector3d vtN = vtV1 ^ vtV2 ;
// normale da prodotto vettoriale
if ( ! vtN.Normalize( EPS_ZERO))
return false ;
m_vTria[nT].vtN = vtN ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Mirror( const Point3d& ptOn, const Vector3d& vtNorm)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità del piano di specchiatura
if ( vtNorm.IsSmall())
return false ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// specchio i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i)
m_vVert[i].ptP.Mirror( ptOn, vtNorm) ;
// inverto le facce
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// inverto la faccia
if ( ! InvertTriangle( i))
return false ;
// aggiorno la normale
if ( ! CalcTriangleNormal( i))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità dei parametri
if ( vtNorm.IsSmall() || vtDir.IsSmall())
return false ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// eseguo scorrimento dei vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.Shear( ptOn, vtNorm, vtDir, dCoeff) ;
}
// aggiorno i triangoli
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// aggiorno la normale
if ( ! CalcTriangleNormal( i))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::ToGlob( const Frame3d& frRef)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// se frame identità, non devo fare alcunché
if ( IsGlobFrame( frRef))
return true ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// trasformo i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.ToGlob( frRef) ;
}
// trasformo le normali dei triangoli
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
if ( m_vTria[i].nIdVert[0] != SVT_DEL)
m_vTria[i].vtN.ToGlob( frRef) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::ToLoc( const Frame3d& frRef)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// se frame identità, non devo fare alcunché
if ( IsGlobFrame( frRef))
return true ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// trasformo i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.ToLoc( frRef) ;
}
// trasformo le normali dei triangoli
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
if ( m_vTria[i].nIdVert[0] != SVT_DEL)
m_vTria[i].vtN.ToLoc( frRef) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::LocToLoc( const Frame3d& frOri, const Frame3d& frDest)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità dei frame
if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR)
return false ;
// se i due riferimenti coincidono, non devo fare alcunché
if ( AreSameFrame( frOri, frDest))
return true ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// trasformo i vertici
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria != SVT_DEL)
m_vVert[i].ptP.LocToLoc( frOri, frDest) ;
}
// trasformo le normali dei triangoli
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
if ( m_vTria[i].nIdVert[0] != SVT_DEL)
m_vTria[i].vtN.LocToLoc( frOri, frDest) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::Invert( void)
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// imposto ricalcolo numero delle parti (le shell non cambiano)
m_vPart.clear() ;
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
// inverto i triangoli
for ( int i = 0 ; i < GetTriangleSize() ; ++ i)
InvertTriangle( i) ;
// se bordi della sfaccettatura validi
if ( m_bFacEdged) {
for ( int nE = 0 ; nE < GetEdgeCount() ; ++ nE) {
// inversione dei vertici nel vettore delle sfaccettature
swap( m_vFacEdge[nE].nIdVert[0], m_vFacEdge[nE].nIdVert[1]) ;
// inversione delle adiacenze nel vettore delle sfaccettature
swap( m_vFacEdge[nE].nIdFacAdj[0], m_vFacEdge[nE].nIdFacAdj[1]) ;
// inversione dell'angolo interno
m_vFacEdge[nE].dIntAng *= -1. ;
}
}
return true ;
}
//----------------------------------------------------------------------------
void
SurfTriMesh::ResetHashGrids3d( void) const
{
if ( m_pHGrd3d != nullptr) {
delete m_pHGrd3d ;
m_pHGrd3d = nullptr ;
m_b3HGrd3d.Reset() ;
}
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::VerifyHashGrids3d( void) const
{
// se già calcolato, non devo fare altro
if ( m_pHGrd3d != nullptr)
return true ;
// alloco
m_pHGrd3d = new HashGrids3d ;
if ( m_pHGrd3d == nullptr)
return false ;
// riempio
const int LIM_HG_TRIA = 127 ;
m_pHGrd3d->SetActivationGrid( GetTriangleCount() > LIM_HG_TRIA) ;
Triangle3d TriaH ;
int nT = GetFirstTriangle( TriaH) ;
while ( nT != SVT_NULL) {
BBox3d boxT ;
TriaH.GetLocalBBox( boxT) ;
m_b3HGrd3d.Add( boxT) ;
if ( ! m_pHGrd3d->Add( nT, boxT)) {
ResetHashGrids3d() ;
return false ;
}
nT = GetNextTriangle( nT, TriaH) ;
}
if ( ! m_pHGrd3d->Update()) {
ResetHashGrids3d() ;
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetAllTriaOverlapBox( const BBox3d& b3Box, INTVECTOR& vT) const
{
if ( ! VerifyHashGrids3d())
return false ;
return m_pHGrd3d->Find( b3Box, vT) ;
}
//----------------------------------------------------------------------------
const BBox3d&
SurfTriMesh::GetAllTriaBox( void) const
{
VerifyHashGrids3d() ;
return m_b3HGrd3d ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::VerifyConnection( bool bShellsAndParts) const
{
if ( ! IsValid())
return false ;
// se non sono già note le shell
if ( m_nShells == -1) {
// reset connessione
for ( auto& Tria : m_vTria)
Tria.nShell = SVT_NULL ;
// ciclo sui triangoli per determinare le shells
m_nShells = 0 ;
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
// salto triangoli cancellati o già assegnati
if ( m_vTria[i].nIdVert[0] == SVT_DEL ||
m_vTria[i].nShell != SVT_NULL)
continue ;
// assegno indice di shell connessa al triangolo
m_vTria[i].nShell = m_nShells ;
++ m_nShells ;
// set di triangoli da aggiornare
set<int> stTria ;
stTria.insert( i) ;
while ( ! stTria.empty()) {
// tolgo un triangolo dal set
const auto iIt = stTria.begin() ;
int nT = *iIt ;
stTria.erase( iIt) ;
// aggiorno i triangoli adiacenti
for ( int j = 0 ; j < 3 ; ++ j) {
int nAdjT = m_vTria[nT].nIdAdjac[j] ;
if ( nAdjT != SVT_NULL && m_vTria[nAdjT].nShell == SVT_NULL) {
m_vTria[nAdjT].nShell = m_vTria[nT].nShell ;
stTria.insert( nAdjT) ;
}
}
}
}
}
// reset delle parti
m_vPart.clear() ;
// se richiesta solo la determinazione delle shell, esco
if ( ! bShellsAndParts)
return true ;
// Se superficie vuota, allora non ho parti
if ( m_nShells == 0)
return true ;
// Se ho solo una shell, allora ho una sola parte
if ( m_nShells == 1) {
m_vPart.emplace_back( m_bClosed, INTVECTOR{ 0}) ;
return true ;
}
// Ho più shell devo controllare la loro posizione relativa
struct SHELLINFO {
SHELLINFO( int nI, double dVol, const BBox3d& b3B, ISurfTriMesh* pStm)
: nInd( nI), dVolume( dVol), b3Box( b3B), pStmShell( pStm) {}
int nInd ;
double dVolume ;
BBox3d b3Box ;
PtrOwner<ISurfTriMesh> pStmShell ;
} ;
vector<SHELLINFO> vOuterShells ;
vector<SHELLINFO> vInnerShells ;
INTVECTOR vOpenShells ;
for ( int nSh = 0 ; nSh < m_nShells ; ++ nSh) {
// se la shell è chiusa
if ( IsShellClosed( nSh)) {
// creo una superficie clonata dalla shell
PtrOwner<ISurfTriMesh> pStmShell( CloneShell( nSh)) ;
if ( IsNull( pStmShell) || ! pStmShell->IsValid())
return false ;
// ne calcolo il volume (con segno)
double dVol = 0. ;
pStmShell->GetVolume( dVol) ;
// ne calcolo il bounding box
BBox3d b3Box ;
pStmShell->GetLocalBBox( b3Box, BBF_STANDARD) ;
// la inserisco nel vettore opportuno
if ( dVol > 0)
vOuterShells.emplace_back( nSh, dVol, b3Box, Release( pStmShell)) ;
else
vInnerShells.emplace_back( nSh, dVol, b3Box, Release( pStmShell)) ;
}
// altrimenti aperta
else {
// la inserisco nel vettore delle shell aperte
vOpenShells.push_back( nSh) ;
}
}
// ordino il vettore delle shell esterne in senso crescente di volume (crescenti anche in valore assoluto)
sort( vOuterShells.begin(), vOuterShells.end(),
[]( const SHELLINFO& a, const SHELLINFO& b) {
return ( a.dVolume < b.dVolume) ;
}) ;
// ordino il vettore delle shell interne in senso crescente di volume (decrescenti in valore assoluto)
sort( vInnerShells.begin(), vInnerShells.end(),
[]( const SHELLINFO& a, const SHELLINFO& b) {
return ( a.dVolume < b.dVolume) ;
}) ;
// classifico le shell esterne
for ( int i = 0 ; i < int( vOuterShells.size()) ; ++ i) {
// inserisco nel vettore delle parti
m_vPart.emplace_back( true, INTVECTOR{ vOuterShells[i].nInd}) ;
// cerco eventuali interne che vi appartengono
CISURFTMPVECTOR vStmTest{ vOuterShells[i].pStmShell} ;
for ( int j = 0 ; j < int( vInnerShells.size()) ; ++ j) {
// se libera e il box è incluso, verifico se realmente interna
if ( vInnerShells[j].nInd >= 0 && vOuterShells[i].b3Box.Encloses( vInnerShells[j].b3Box)) {
Point3d ptCheck ;
vInnerShells[j].pStmShell->GetFirstVertex( ptCheck) ;
bool bIsInside = true ;
for ( const auto& pStmTest : vStmTest) {
BBox3d b3Test = pStmTest->GetAllTriaBox() ;
if ( b3Test.Overlaps( vInnerShells[j].b3Box)) {
DistPointSurfTm distCalculator( ptCheck, *pStmTest) ;
if ( ! distCalculator.IsPointInside()) {
bIsInside = false ;
break ;
}
}
}
if ( bIsInside) {
m_vPart.back().vShell.push_back( vInnerShells[j].nInd) ;
vInnerShells[j].nInd = -1 ;
vStmTest.push_back( vInnerShells[j].pStmShell) ;
}
}
}
}
// classifico le shell interne rimaste libere
bool bNewInner = true ;
CISURFTMPVECTOR vStmTest ;
for ( int i = 0 ; i < int( vInnerShells.size()) ; ++ i) {
if ( vInnerShells[i].nInd >= 0) {
// se non è nuova interna verifico sia interna alle altre correnti
if ( ! bNewInner) {
bool bIsInside = true ;
Point3d ptCheck ;
vInnerShells[i].pStmShell->GetFirstVertex( ptCheck) ;
// se il box interferisce con altri, verifico se realmente interna
for ( const auto& pStmTest : vStmTest) {
BBox3d b3Test = pStmTest->GetAllTriaBox() ;
if ( b3Test.Overlaps( vInnerShells[i].b3Box)) {
DistPointSurfTm distCalculator( ptCheck, *pStmTest) ;
if ( ! distCalculator.IsPointInside()) {
bIsInside = false ;
break ;
}
}
}
if ( bIsInside)
m_vPart.back().vShell.push_back( vInnerShells[i].nInd) ;
else
bNewInner = true ;
}
// se nuova interna
if ( bNewInner) {
bNewInner = false ;
m_vPart.emplace_back( true, INTVECTOR{ -1, vInnerShells[i].nInd}) ;
vStmTest.clear() ;
}
vInnerShells[i].nInd = -1 ;
vStmTest.push_back( vInnerShells[i].pStmShell) ;
}
}
// aggiungo all'elenco delle parti le shell aperte
for ( int i = 0 ; i < int( vOpenShells.size()) ; ++ i) {
m_vPart.emplace_back( false, INTVECTOR{ vOpenShells[i]}) ;
}
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetShellCount( void) const
{
if ( ! IsValid())
return 0 ;
if ( m_nShells == -1 && ! VerifyConnection())
return 0 ;
return m_nShells ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetShellArea( int nShell, double& dArea) const
{
// Controllo parametro di ritorno
if ( &dArea == nullptr)
return false ;
// Imposto area nulla
dArea = 0 ;
// Verifiche sull'oggetto
if ( ! IsValid())
return false ;
if ( m_nShells == -1 && ! VerifyConnection())
return false ;
// Se la componente non esiste, errore
if ( nShell < 0 || nShell >= m_nShells)
return false ;
// sommo l'area di tutti i triangoli della shell
Triangle3d Tria ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
if ( m_vTria[nId].nShell == nShell)
dArea += Tria.GetArea() ;
nId = GetNextTriangle( nId, Tria) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::IsShellClosed( int nShell) const
{
// Verifiche sull'oggetto
if ( ! IsValid())
return false ;
if ( m_nShells == -1 && ! VerifyConnection( false))
return false ;
// Se la componente non esiste, errore
if ( nShell < 0 || nShell >= m_nShells)
return false ;
// ciclo sui triangoli della shell
bool bClosed = true ;
for ( int i = 0 ; i < GetTriangleSize() ; ++ i) {
// se triangolo non cancellato e della shell
if ( m_vTria[i].nIdVert[0] != SVT_DEL && m_vTria[i].nShell == nShell) {
// verifico le adiacenze
if ( m_vTria[i].nIdAdjac[0] == SVT_NULL ||
m_vTria[i].nIdAdjac[1] == SVT_NULL ||
m_vTria[i].nIdAdjac[2] == SVT_NULL) {
bClosed = false ;
break ;
}
}
}
// restituisco il risultato
return bClosed ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::RemoveShell( int nShell)
{
if ( ! IsValid())
return false ;
// Il numero delle componenti deve essere maggiore di zero o calcolabile
if ( m_nShells == -1 && ! VerifyConnection())
return false ;
// Se la componente non esiste, errore
if ( nShell < 0 || nShell >= m_nShells)
return false ;
// Rimuovo i triangoli della componente nShell
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
if ( m_vTria[i].nShell == nShell)
RemoveTriangle( i) ;
}
// I dati di Shells e Parts sono stati resettati da RemoveTriangle
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
return true ;
}
//----------------------------------------------------------------------------
SurfTriMesh*
SurfTriMesh::CloneShell( int nShell) const
{
if ( ! IsValid())
return nullptr ;
// Il numero delle componenti deve essere maggiore di zero o calcolabile
if ( m_nShells == -1 && ! VerifyConnection())
return nullptr ;
// Se la componente non esiste, errore
if ( nShell < 0 || nShell >= m_nShells)
return nullptr ;
// Creo nuovo oggetto SurfTriMesh
PtrOwner<SurfTriMesh> pSurfTM( new( nothrow) SurfTriMesh) ;
if ( IsNull( pSurfTM))
return nullptr ;
// Copio il valore dei membri
pSurfTM->m_dLinTol = m_dLinTol ;
pSurfTM->m_dBoundaryAng = m_dBoundaryAng ;
pSurfTM->m_dCosBndAng = m_dCosBndAng ;
pSurfTM->m_dSmoothAng = m_dSmoothAng ;
pSurfTM->m_dCosSmAng = m_dCosSmAng ;
// Copio i triangoli
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
if ( m_vTria[i].nIdVert[0] != SVT_DEL && m_vTria[i].nShell == nShell) {
int nNewInd[3] = { pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[0]].ptP),
pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[1]].ptP),
pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[2]].ptP)} ;
if ( pSurfTM->AddTriangle( nNewInd, m_vTria[i].nTFlag) == SVT_NULL)
return nullptr ;
}
}
// Aggiusto la superficie
pSurfTM->DoCompacting() ;
// Restituisco la nuova superficie
return Release( pSurfTM) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetPartLocalBBox( int nP, BBox3d& b3Loc) const
{
// verifico esistenza della parte
if ( nP < 0 || nP >= GetPartCount())
return false ;
// assegno il box in locale
b3Loc.Reset() ;
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
// recupero il vettore delle Shell della parte corrente
const auto& vShell = m_vPart[nP].vShell ;
// scorro i triangoli validi contenuti nella Shell
if ( m_vTria[i].nIdVert[0] != SVT_DEL &&
find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) {
// ciclo sui tre vertici
for ( int j = 0 ; j < 3 ; ++ j)
b3Loc.Add( m_vVert[m_vTria[i].nIdVert[j]].ptP) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetPartBBox( int nP, const Frame3d& frRef, BBox3d& b3Ref) const
{
// verifico esistenza della parte
if ( nP < 0 || nP >= GetPartCount())
return false ;
// assegno il box in locale
b3Ref.Reset() ;
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
// recupero il vettore delle Shell della parte corrente
const auto& vShell = m_vPart[nP].vShell ;
// scorro i triangoli validi contenuti nella Shell
if ( m_vTria[i].nIdVert[0] != SVT_DEL &&
find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) {
// ciclo sui tre vertici
for ( int j = 0 ; j < 3 ; ++ j)
b3Ref.Add( GetToGlob( m_vVert[m_vTria[i].nIdVert[j]].ptP, frRef)) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
int
SurfTriMesh::GetPartCount( void) const
{
if ( ! IsValid())
return 0 ;
if ( m_vPart.empty() && ! VerifyConnection())
return 0 ;
return int( m_vPart.size()) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetPartArea( int nPart, double& dArea) const
{
// Controllo parametro di ritorno
if ( &dArea == nullptr)
return false ;
// Imposto area nulla
dArea = 0 ;
// Verifiche sull'oggetto
if ( ! IsValid())
return false ;
if ( m_vPart.empty() && ! VerifyConnection())
return false ;
// Se la componente non esiste, errore
if ( nPart < 0 || nPart >= int( m_vPart.size()))
return false ;
// sommo l'area di tutti i triangoli della parte
Triangle3d Tria ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
const auto& vShell = m_vPart[nPart].vShell ;
if ( find( vShell.begin(), vShell.end(), m_vTria[nId].nShell) != vShell.end())
dArea += Tria.GetArea() ;
nId = GetNextTriangle( nId, Tria) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::IsPartClosed( int nPart) const
{
// Verifiche sull'oggetto
if ( ! IsValid())
return false ;
if ( m_vPart.empty() && ! VerifyConnection())
return false ;
// Se la componente non esiste, errore
if ( nPart < 0 || nPart >= int( m_vPart.size()))
return false ;
return m_vPart[nPart].bClosed ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetPartVolume( int nPart, double& dVolume) const
{
// Controllo parametro di ritorno
if ( &dVolume == nullptr)
return false ;
// Imposto volume nullo
dVolume = 0 ;
// Verifiche sull'oggetto
if ( ! IsValid())
return false ;
if ( m_vPart.empty() && ! VerifyConnection())
return false ;
// Se la componente non esiste, errore
if ( nPart < 0 || nPart >= int( m_vPart.size()))
return false ;
// la parte deve essere chiusa
if ( ! m_vPart[nPart].bClosed)
return true ;
// sommo il volume con segno di tutte le piramidi della parte dall'origine ad ogni faccia
Triangle3d Tria ;
int nId = GetFirstTriangle( Tria) ;
while ( nId != SVT_NULL) {
const auto& vShell = m_vPart[nPart].vShell ;
if ( find( vShell.begin(), vShell.end(), m_vTria[nId].nShell) != vShell.end()) {
Vector3d vtA = ( Tria.GetP( 1) - Tria.GetP( 0)) ^ ( Tria.GetP( 2) - Tria.GetP( 0)) ;
dVolume += ( Tria.GetP( 0) - ORIG) * vtA ;
}
nId = GetNextTriangle( nId, Tria) ;
}
dVolume /= 6 ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetPartLoops( int nPart, POLYLINEVECTOR& vPL) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
vPL.clear() ;
// aggiorno timestamp dei triangoli
++ m_nTimeStamp ;
for ( auto& Tria : m_vTria)
Tria.nTemp = m_nTimeStamp ;
// incremento time stamp
++ m_nTimeStamp ;
// recupero il vettore delle Shell che compongono la part
const auto& vShell = m_vPart[nPart].vShell ;
// ciclo sui triangoli
for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) {
// se triangolo valido e non ancora visitato
if ( m_vTria[nT].nIdVert[0] != SVT_DEL && m_vTria[nT].nTemp != m_nTimeStamp &&
find( vShell.begin(), vShell.end(), m_vTria[nT].nShell) != vShell.end()) {
// determino i triangoli adiacenti
int nAdjT[3] ;
for ( int j = 0 ; j < 3 ; ++ j)
nAdjT[j] = m_vTria[nT].nIdAdjac[j] ;
// se tutti e tre i lati sono di contorno
if ( nAdjT[0] == SVT_NULL &&
nAdjT[1] == SVT_NULL &&
nAdjT[2] == SVT_NULL) {
// ho trovato un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
}
// se i due lati 0 e 1 sono di contorno
else if ( nAdjT[0] == SVT_NULL &&
nAdjT[1] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back()))
return false ;
}
// se i due lati 1 e 2 sono di contorno
else if ( nAdjT[1] == SVT_NULL &&
nAdjT[2] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back()))
return false ;
}
// se i due lati 2 e 0 sono di contorno
else if ( nAdjT[2] == SVT_NULL &&
nAdjT[0] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back()))
return false ;
}
// se il lato 0 è di contorno
else if ( nAdjT[0] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 1, m_nTimeStamp, vPL.back()))
return false ;
}
// se il lato 1 è di contorno
else if ( nAdjT[1] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 2, m_nTimeStamp, vPL.back()))
return false ;
}
// se il lato 2 è di contorno
else if ( nAdjT[2] == SVT_NULL) {
// ho trovato l'inizio di un loop
vPL.emplace_back() ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ;
vPL.back().AddUPoint( nT, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ;
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
// cammino lungo il loop fino a chiuderlo
if ( ! MarchAlongLoop( nT, 0, m_nTimeStamp, vPL.back()))
return false ;
}
// altrimenti non c'è contorno
else {
// marco il triangolo come verificato
m_vTria[nT].nTemp = m_nTimeStamp ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::RemovePart( int nPart)
{
if ( ! IsValid())
return false ;
// Il numero delle componenti deve essere maggiore di zero o calcolabile
if ( m_vPart.empty() && ! VerifyConnection())
return false ;
// Se la componente non esiste, errore
if ( nPart < 0 || nPart >= int( m_vPart.size()))
return false ;
// Rimuovo i triangoli della componente nPart (ovvero delle sue shell)
const auto vShell = m_vPart[nPart].vShell ;
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
if ( find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end())
RemoveTriangle( i) ;
}
// I dati di Shells e Parts sono stati resettati da RemoveTriangle
// imposto ricalcolo della grafica e di hashgrids3d
m_OGrMgr.Reset() ;
ResetHashGrids3d() ;
return true ;
}
//----------------------------------------------------------------------------
SurfTriMesh*
SurfTriMesh::ClonePart( int nPart) const
{
if ( ! IsValid())
return nullptr ;
// Il numero delle parti deve essere maggiore di zero o calcolabile
if ( m_vPart.empty() && ! VerifyConnection())
return nullptr ;
// Se la parte non esiste, errore
if ( nPart < 0 || nPart >= int( m_vPart.size()))
return nullptr ;
// Creo nuovo oggetto SurfTriMesh
PtrOwner<SurfTriMesh> pSurfTM( new( nothrow) SurfTriMesh) ;
if ( IsNull( pSurfTM))
return nullptr ;
// Copio il valore dei membri
pSurfTM->m_dLinTol = m_dLinTol ;
pSurfTM->m_dBoundaryAng = m_dBoundaryAng ;
pSurfTM->m_dCosBndAng = m_dCosBndAng ;
pSurfTM->m_dSmoothAng = m_dSmoothAng ;
pSurfTM->m_dCosSmAng = m_dCosSmAng ;
// Copio i triangoli
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i) {
const auto& vShell = m_vPart[nPart].vShell ;
if ( m_vTria[i].nIdVert[0] != SVT_DEL &&
find( vShell.begin(), vShell.end(), m_vTria[i].nShell) != vShell.end()) {
int nNewInd[3] = { pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[0]].ptP),
pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[1]].ptP),
pSurfTM->AddVertex( m_vVert[m_vTria[i].nIdVert[2]].ptP)} ;
if ( pSurfTM->AddTriangle( nNewInd, m_vTria[i].nTFlag) == SVT_NULL)
return nullptr ;
}
}
// Aggiusto la superficie
pSurfTM->DoCompacting() ;
// Restituisco la nuova superficie
return Release( pSurfTM) ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetPartAndShellFromFacet( int nFacet, int& nPart, int& nShell) const
{
// l'indice della faccia deve essere nei limiti
if ( nFacet < 0 || nFacet >= int( m_vFacet.size()))
return false ;
// mi assicuro che siano calcolate il numero di parti e di shell
int nParts = GetPartCount() ;
int nTria = m_vFacet[nFacet] ;
nShell = m_vTria[nTria].nShell ;
// scopro in quale part è la shell
int nPartTemp = 0 ;
nPart = - 1 ;
while ( nPartTemp < nParts && nPart == - 1) {
for ( int i : m_vPart[nPartTemp].vShell) {
if ( i == nShell) {
nPart = nPartTemp ;
break ;
}
}
++nPartTemp ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::ResetTFlags( void)
{
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i)
m_vTria[i].nTFlag = 0 ;
m_nMaxTFlag = 0 ;
m_OGrMgr.Clear() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::ResetTempInts( void) const
{
for ( int i = 0 ; i < int( m_vTria.size()) ; ++ i)
m_vTria[i].nTemp = 0 ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::SetTFlag( int nId, int nTFlag)
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
m_vTria[nId].nTFlag = nTFlag ;
m_nMaxTFlag = max( m_nMaxTFlag, abs( nTFlag)) ;
m_OGrMgr.Clear() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::SetTempInt( int nId, int nTempInt) const
{
// verifico esistenza del triangolo
if ( nId < 0 || nId >= GetTriangleSize() || m_vTria[nId].nIdVert[0] == SVT_DEL)
return false ;
m_vTria[nId].nTemp = nTempInt ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol)
{
// scorro i triangoli
for ( const Triangle3dEx& Tria : vTria) {
// ciclo sui tre vertici
int nIdV[3]{} ;
for ( int nV = 0 ; nV < 3 ; ++ nV) {
// verifico se vertice già presente
int nId ;
if ( ! VertGrid.Find( Tria.GetP( nV), dVertexTol, nId)) {
// se non presente, lo aggiugo
nIdV[nV] = AddVertex( Tria.GetP( nV)) ;
if ( nIdV[nV] == SVT_NULL)
return false ;
VertGrid.InsertPoint( Tria.GetP( nV), nIdV[nV]) ;
}
else
nIdV[nV] = nId ;
}
// se i vertici sono tutti diversi tra loro, inserisco il triangolo
if ( nIdV[0] != nIdV[1] && nIdV[0] != nIdV[2] && nIdV[1] != nIdV[2]) {
int nT = AddTriangle( nIdV, Tria.GetGrade()) ;
if ( nT != SVT_NULL && nT != SVT_DEL) {
// associo relazione vertice-triangolo
for ( int i = 0 ; i < 3 ; ++ i)
m_vVert[nIdV[i]].nIdTria = nT ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::AdjustTopologyFromZMap( void)
{
// cancello tutti i vertici che puntano a triangoli cancellati
bool bPack = false ;
for ( int i = 0 ; i < GetVertexSize() ; ++ i) {
if ( m_vVert[i].nIdTria == SVT_NULL) {
m_vVert[i].nIdTria = SVT_DEL ;
bPack = true ;
}
}
if ( bPack)
PackVertices() ;
m_nStatus = SurfTriMesh::OK ;
// calcolo le adiacenze
if ( ! AdjustAdjacencies())
return false ;
// verifico l'orientamento ( TODO -- Da migliorare...)
// Ora per funzionare è necessario che il ciclo sui triangoli parta da un triangolo orientato
// correttamente e che i triangoli invertiti siano "circondati" da triangoli tutti orientati correttamente
if ( ! AdjustOrientations())
return false ;
// calcolo le facce
if ( ! VerifyFaceting())
return false ;
// rimozione delle TJunction
bool bModified = false ;
if ( ! RemoveTJunctions( bModified, SQ_EPS_SMALL))
return false ;
if ( bModified) {
if ( ! AdjustVertices() || ! DoCompacting())
return false ;
}
else
TestSealing() ;
// semplifico le facce ( anche più piccola)
if ( ! SimplifyFacets( MAX_EDGE_LEN_STD, false, 5. * EPS_SMALL))
LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::AdjustTopologyFromZMap")
return true ;
}
//----------------------------------------------------------------------------
bool
SurfTriMesh::GetTriaFromFace( int nF, int& nT) const
{
// la superficie deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico stato sfaccettatura
if ( ! VerifyFaceting())
return false ;
// l'indice della faccia deve essere nei limiti
if ( nF < 0 || nF >= int( m_vFacet.size()))
return false ;
// recupero la normale di un triangolo della faccetta
nT = m_vFacet[nF] ;
return true ;
}