fca2574f79
- aggiunto fattore di scalatura a ImportStl - uso di PointGrid3d in import STL invece di ordinamento lessicografico.
407 lines
12 KiB
C++
407 lines
12 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2014-2014
|
|
//----------------------------------------------------------------------------
|
|
// File : ImportStl.cpp Data : 04.04.14 Versione : 1.5d1
|
|
// Contenuto : Implementazione della classe per l'importazione di STL.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 04.04.14 DS Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "ImportStl.h"
|
|
#include "DllMain.h"
|
|
#include "/EgtDev/Include/EgtPointerOwner.h"
|
|
#include "/EgtDEv/Include/EGnScan.h"
|
|
#include "/EgtDev/Include/EgnStringUtils.h"
|
|
#include "/EgtDev/Include/EgnStringConverter.h"
|
|
#include "/EgtDev/Include/EGkPointGrid3d.h"
|
|
#include "/EgtDev/Include/EGkGeomDB.h"
|
|
#include "/EgtDev/Include/EGkSurfTriMesh.h"
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
static const int GRID_NUM_BUCKETS = 50000 ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
IImportStl*
|
|
CreateImportStl( void)
|
|
{
|
|
return static_cast<IImportStl*> ( new(nothrow) ImportStl) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
ImportStl::Import( const string& sFile, IGeomDB* pGDB, int nIdGroup, double dScaleFactor)
|
|
{
|
|
// verifico il DB geometrico
|
|
if ( pGDB == nullptr) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error on GeomDB")
|
|
return false ;
|
|
}
|
|
m_pGDB = pGDB ;
|
|
|
|
// verifico l'Id di gruppo
|
|
if ( ! m_pGDB->ExistsObj( nIdGroup)) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error on IdGroup")
|
|
return false ;
|
|
}
|
|
m_nIdGroup = nIdGroup ;
|
|
|
|
// verifico il fattore di scala
|
|
if ( dScaleFactor < EPS_SMALL) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error on ScaleFactor too small (minimum 0.001).")
|
|
return false ;
|
|
}
|
|
m_dScaleFactor = dScaleFactor ;
|
|
|
|
// eseguo l'opportuna importazione
|
|
switch ( StlType( sFile)) {
|
|
case STL_ASCII :
|
|
return ImportASCII( sFile) ;
|
|
case STL_BINARY :
|
|
return ImportBinary( sFile) ;
|
|
default :
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
ImportStl::StlType( const std::string& sFile)
|
|
{
|
|
// apertura del file di ingresso
|
|
ifstream InFile ;
|
|
InFile.open( stringtoW( sFile), ios::in | ios::binary) ;
|
|
if ( InFile.fail())
|
|
return STL_ERROR ;
|
|
|
|
// lettura dei primi 256 byte
|
|
char cBuff[256] ;
|
|
InFile.read( cBuff, 255) ;
|
|
cBuff[InFile.gcount()] = '\0' ;
|
|
|
|
// chiusura del file
|
|
InFile.close() ;
|
|
|
|
// verifico se iniziano con "solid" e poi "facet" e "normal"
|
|
string sBuff = cBuff ;
|
|
size_t nPosS = sBuff.find( "solid") ;
|
|
size_t nPosF = sBuff.find( "facet") ;
|
|
size_t nPosN = sBuff.find( "normal") ;
|
|
if ( nPosS != string::npos && nPosS < 10 &&
|
|
nPosF != string::npos &&
|
|
nPosN != string::npos)
|
|
return STL_ASCII ;
|
|
else
|
|
return STL_BINARY ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
ImportStl::ImportASCII( const string& sFile)
|
|
{
|
|
// inizializzo lo scanner
|
|
Scanner TheScanner ;
|
|
if ( ! TheScanner.Init( sFile)) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error on Init")
|
|
return false ;
|
|
}
|
|
|
|
// ciclo di lettura degli oggetti
|
|
bool bOk = true ;
|
|
bool bEnd = false ;
|
|
do {
|
|
if ( ! LoadSolid( TheScanner, bEnd)) {
|
|
bOk = false ;
|
|
string sOut = "ImportStl : Error on line " + ToString( TheScanner.GetCurrLineNbr()) ;
|
|
LOG_ERROR( GetEExLogger(), sOut.c_str())
|
|
}
|
|
} while ( bOk && ! bEnd) ;
|
|
|
|
return bOk ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
ImportStl::LoadSolid( Scanner& TheScanner, bool& bEnd)
|
|
{
|
|
string sLine ;
|
|
STRVECTOR vsParams ;
|
|
|
|
// recupero la prima linea
|
|
if ( ! TheScanner.GetLine( sLine)) {
|
|
bEnd = true ;
|
|
return true ;
|
|
}
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 1 parametro
|
|
if ( vsParams.size() < 1)
|
|
return false ;
|
|
// deve essere la parola chiave "solid"
|
|
if ( vsParams[0] != "solid")
|
|
return false ;
|
|
// se c'è un secondo parametro è il nome
|
|
string sName ;
|
|
if ( vsParams.size() >= 2)
|
|
sName = vsParams[1] ;
|
|
|
|
// preparo l'oggetto TriMesh
|
|
PtrOwner<ISurfTriMesh> pSTM( CreateSurfTriMesh()) ;
|
|
if ( ! IsValid( pSTM)) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error allocating SurfTriMesh")
|
|
return false ;
|
|
}
|
|
if ( ! pSTM->Init( 3, 1)) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error initialising SurfTriMesh")
|
|
return false ;
|
|
}
|
|
|
|
// Grid dei vertici
|
|
PointGrid3d VertGrid ;
|
|
VertGrid.Init( GRID_NUM_BUCKETS) ;
|
|
|
|
// ciclo di lettura dei triangoli
|
|
bool bOk = true ;
|
|
bool bTEnd = false ;
|
|
do {
|
|
if ( ! LoadTriangle( TheScanner, Get( pSTM), VertGrid, bTEnd)) {
|
|
bOk = false ;
|
|
string sOut = "ImportStl : Error on line " + ToString( TheScanner.GetCurrLineNbr()) ;
|
|
LOG_ERROR( GetEExLogger(), sOut.c_str())
|
|
}
|
|
} while ( bOk && ! bTEnd) ;
|
|
|
|
// recupero la linea di terminazione dell'oggetto
|
|
if ( ! TheScanner.GetLine( sLine))
|
|
return false ;
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 1 parametro
|
|
if ( vsParams.size() < 1)
|
|
return false ;
|
|
// deve essere la parola chiave "endsolid" oppure "end" "solid"
|
|
if ( vsParams[0] != "endsolid" && vsParams[0] != "end")
|
|
return false ;
|
|
|
|
// valido la superficie e calcolo le adiacenze
|
|
if ( ! pSTM->AdjustTopology()) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error adjusting topology in SurfTriMesh")
|
|
return false ;
|
|
}
|
|
// inserisco l'oggetto nel DB geometrico
|
|
int nIdNew = m_pGDB->AddGeoObj( GDB_ID_NULL, m_nIdGroup, Release( pSTM)) ;
|
|
// se previsto il nome, lo assegno
|
|
if ( ! sName.empty())
|
|
m_pGDB->SetName( nIdNew, sName) ;
|
|
|
|
bEnd = false ;
|
|
return ( nIdNew != GDB_ID_NULL) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
ImportStl::LoadTriangle( Scanner& TheScanner, ISurfTriMesh* pSTM, PointGrid3d& VertGrid, bool& bEnd)
|
|
{
|
|
string sLine ;
|
|
STRVECTOR vsParams ;
|
|
|
|
// recupero la prima linea
|
|
if ( ! TheScanner.GetLine( sLine))
|
|
return false ;
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 1 parametro
|
|
if ( vsParams.size() < 1)
|
|
return false ;
|
|
// deve essere la parola chiave "facet"
|
|
if ( vsParams[0] != "facet") {
|
|
TheScanner.UngetLine( sLine) ;
|
|
bEnd = true ;
|
|
return true ;
|
|
}
|
|
// ignoro il resto
|
|
|
|
// recupero la seconda linea
|
|
if ( ! TheScanner.GetLine( sLine))
|
|
return false ;
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 2 parametri
|
|
if ( vsParams.size() < 2)
|
|
return false ;
|
|
// devono essere le parole chiave "outer" e "loop"
|
|
if ( vsParams[0] != "outer" || vsParams[1] != "loop")
|
|
return false ;
|
|
|
|
// ciclo sui tre vertici
|
|
int nIdV[3] ;
|
|
for ( int i = 0 ; i < 3 ; ++ i) {
|
|
// recupero una linea
|
|
if ( ! TheScanner.GetLine( sLine))
|
|
return false ;
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 4 parametri
|
|
if ( vsParams.size() < 4)
|
|
return false ;
|
|
// parola chiave "vertex"
|
|
if ( vsParams[0] != "vertex")
|
|
return false ;
|
|
// tre coordinate del vertice
|
|
Point3d ptP ;
|
|
if ( ! FromString( vsParams[1], ptP.x) ||
|
|
! FromString( vsParams[2], ptP.y) ||
|
|
! FromString( vsParams[3], ptP.z))
|
|
return false ;
|
|
// scalo il vertice
|
|
ptP *= m_dScaleFactor ;
|
|
// verifico se vertice già presente
|
|
int nId ;
|
|
if ( ! VertGrid.Find( ptP, 2 * EPS_SMALL, nId)) {
|
|
// aggiungo il vertice
|
|
if ( ( nIdV[i] = pSTM->AddVertex( ptP)) == SVT_NULL)
|
|
return false ;
|
|
VertGrid.InsertPoint( ptP, nIdV[i]) ;
|
|
}
|
|
else
|
|
nIdV[i] = 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]) {
|
|
if ( pSTM->AddTriangle( nIdV) == SVT_NULL)
|
|
return false ;
|
|
}
|
|
|
|
// nuova linea
|
|
if ( ! TheScanner.GetLine( sLine))
|
|
return false ;
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 1 parametro
|
|
if ( vsParams.size() < 1)
|
|
return false ;
|
|
// parola chiave "endloop"
|
|
if ( vsParams[0] != "endloop")
|
|
return false ;
|
|
|
|
// nuova linea
|
|
if ( ! TheScanner.GetLine( sLine))
|
|
return false ;
|
|
// la divido in parametri
|
|
Tokenize( sLine, " ", vsParams) ;
|
|
// almeno 1 parametro
|
|
if ( vsParams.size() < 1)
|
|
return false ;
|
|
// parola chiave "endfacet"
|
|
if ( vsParams[0] != "endfacet")
|
|
return false ;
|
|
|
|
bEnd = false ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
ImportStl::ImportBinary( const string& sFile)
|
|
{
|
|
// apertura del file di ingresso
|
|
ifstream InFile ;
|
|
InFile.open( stringtoW( sFile), ios::in | ios::binary) ;
|
|
if ( InFile.fail())
|
|
return false ;
|
|
|
|
// lettura dei primi ottanta byte (commento da scartare)
|
|
const int LEN_REM = 80 ;
|
|
char cRem[LEN_REM] ;
|
|
InFile.read( cRem, LEN_REM) ;
|
|
if ( InFile.gcount() != LEN_REM)
|
|
return false ;
|
|
|
|
// lettura del numero di triangoli
|
|
const int LEN_INT = 4 ;
|
|
union { int i; char c[LEN_INT]; } uTria ;
|
|
InFile.read( uTria.c, LEN_INT) ;
|
|
if ( InFile.gcount() != LEN_INT)
|
|
return false ;
|
|
|
|
// preparo l'oggetto TriMesh
|
|
PtrOwner<ISurfTriMesh> pSTM( CreateSurfTriMesh()) ;
|
|
if ( ! IsValid( pSTM)) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error allocating SurfTriMesh")
|
|
return false ;
|
|
}
|
|
if ( ! pSTM->Init( 3, 1)) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error initialising SurfTriMesh")
|
|
return false ;
|
|
}
|
|
|
|
// Grid dei vertici
|
|
PointGrid3d VertGrid ;
|
|
VertGrid.Init( GRID_NUM_BUCKETS) ;
|
|
|
|
// lettura dei triangoli
|
|
for ( int j = 0 ; j < uTria.i ; ++ j) {
|
|
// salto i 3 float per la normale
|
|
const int LEN_NRM = 12 ;
|
|
char cNorm[LEN_NRM] ;
|
|
InFile.read( cNorm, LEN_NRM) ;
|
|
if ( InFile.gcount() != LEN_NRM)
|
|
return false ;
|
|
// ciclo sui tre vertici
|
|
int nIdV[3] ;
|
|
for ( int i = 0 ; i < 3 ; ++ i) {
|
|
const int LEN_3F = 12 ;
|
|
union { float f[3]; char c[LEN_3F]; } ufP ;
|
|
InFile.read( ufP.c, LEN_3F) ;
|
|
if ( InFile.gcount() != LEN_3F)
|
|
return false ;
|
|
Point3d ptP( ufP.f[0], ufP.f[1], ufP.f[2]) ;
|
|
// scalo il vertice
|
|
ptP *= m_dScaleFactor ;
|
|
// verifico se vertice già presente
|
|
int nId ;
|
|
if ( ! VertGrid.Find( ptP, 2 * EPS_SMALL, nId)) {
|
|
// aggiungo il vertice
|
|
if ( ( nIdV[i] = pSTM->AddVertex( ptP)) == SVT_NULL)
|
|
return false ;
|
|
VertGrid.InsertPoint( ptP, nIdV[i]) ;
|
|
}
|
|
else
|
|
nIdV[i] = 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]) {
|
|
if ( pSTM->AddTriangle( nIdV) == SVT_NULL)
|
|
return false ;
|
|
}
|
|
// salto 2 byte
|
|
const int LEN_SKI = 2 ;
|
|
char cSkip[LEN_SKI] ;
|
|
InFile.read( cSkip, LEN_SKI) ;
|
|
if ( InFile.gcount() != LEN_SKI)
|
|
return false ;
|
|
}
|
|
|
|
// valido la superficie e calcolo le adiacenze
|
|
if ( ! pSTM->AdjustTopology()) {
|
|
LOG_ERROR( GetEExLogger(), "ImportStl : Error adjusting topology in SurfTriMesh")
|
|
return false ;
|
|
}
|
|
// inserisco l'oggetto nel DB geometrico
|
|
int nIdNew = m_pGDB->AddGeoObj( GDB_ID_NULL, m_nIdGroup, Release( pSTM)) ;
|
|
|
|
// chiusura del file
|
|
InFile.close() ;
|
|
|
|
return ( nIdNew != GDB_ID_NULL) ;
|
|
}
|