61a5e53351
- sistemazioni varie legate a scalature per aumentare la precisione.
4517 lines
154 KiB
C++
4517 lines
154 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
|
|
vtN = 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[vT[nPos]].vtN * m_vTria[vT[i]].vtN >= m_dCosSmAng)
|
|
vtN += 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[vT[nPos]].vtN * m_vTria[vT[i]].vtN >= m_dCosSmAng)
|
|
vtN += m_vTria[vT[i]].vtN ;
|
|
else
|
|
break ;
|
|
}
|
|
vtN.Normalize() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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::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 ;
|
|
}
|