Files
EgtExchange/Import3MF.cpp
T
Sara Perdoncin 0885c8b371 EgtExchange 2.3b1 :
- import 3MF prima implementazione.
2021-02-05 16:38:57 +00:00

915 lines
34 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2014-2014
//----------------------------------------------------------------------------
// File : Import3MF.h Data : 29.01.21 Versione : 2.3b1
// Contenuto : Dichiarazione della classe Import3MF.
//
//
//
// Modifiche : 21.01.21 SP Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "Import3MF.h"
#include "DllMain.h"
#include "pugixml.hpp"
#include "/EgtDev/Include/EExDllMain.h"
#include "/EgtDev/Include/EGkStmFromTriangleSoup.h"
#include "/EgtDev/Extern/libzip/Include/zip.h"
using namespace std ;
//----------------------------------------------------------------------------
IImport3MF*
CreateImport3MF( void)
{
// verifico la chiave e le opzioni
if ( ! TestKeyForEEx( GetEExKey(), KEYOPT_EEX_INPBASE, GetEExLogger()))
return nullptr ;
// creo l'oggetto
return static_cast<IImport3MF*> ( new( nothrow) Import3MF) ;
}
//----------------------------------------------------------------------------
bool
Import3MF::Import( const string& sFile, IGeomDB* pGDB, int nIdGroup)
{
LOG_INFO( GetEExLogger(), ( "Import3MF : " + sFile).c_str()) ;
// verifico il DB geometrico
if ( pGDB == nullptr) {
LOG_ERROR( GetEExLogger(), " Import3MF : Error on GeomDB") ;
return false ;
}
m_pGDB = pGDB ;
// verifico l'Id di gruppo
if ( ! m_pGDB->ExistsObj( nIdGroup)) {
LOG_ERROR( GetEExLogger(), " Import3MF : Error on IdGroup") ;
return false ;
}
m_nIdGroup = nIdGroup ;
// Apertura file 3MF
int nErrorZip ;
m_ziparchive = zip_open( sFile.c_str(), ZIP_RDONLY, &nErrorZip) ;
if ( m_ziparchive == nullptr){
string sError = " Import3MF : Error opening file. " ;
if ( nErrorZip == ZIP_ER_INVAL)
sError += "Invalid path" ;
else if ( nErrorZip == ZIP_ER_MEMORY)
sError += "Required memory could not be allocated" ;
else if ( nErrorZip == ZIP_ER_NOENT)
sError += "Path does not exist" ;
else if ( nErrorZip == ZIP_ER_NOZIP)
sError += "File is not a zip archive" ;
else if ( nErrorZip == ZIP_ER_OPEN)
sError += "Path could not be opened" ;
else if ( nErrorZip == ZIP_ER_READ)
sError += "A read error occurred" ;
LOG_ERROR( GetEExLogger() , sError.c_str()) ;
}
// Apertura del file con il modello 3D
string sModelFileName = "3D/3dmodel.model" ;
struct zip_stat sb ;
if ( zip_stat( m_ziparchive, sModelFileName.c_str(), 0, &sb) == -1){
// se il nome del file non è standard lo cerco dal file .rels
sModelFileName = FindFileName() ;
if ( zip_stat( m_ziparchive, sModelFileName.c_str(), 0, &sb) == -1){
LOG_ERROR( GetEExLogger(), " Import3MF : file .model not found") ;
return false ;
}
}
zip_file_t * file = zip_fopen( m_ziparchive, sModelFileName.c_str(), ZIP_FL_UNCHANGED) ;
if ( file == nullptr){
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening .model file") ;
return false ;
}
size_t filesize = ( size_t) sb.size ;
char * contents = new char[filesize] ;
if ( zip_fread( file, contents, sb.size) == -1){
LOG_ERROR( GetEExLogger(), " Import3MF : Error reading .model file") ;
return false ;
}
// Lettura file
pugi::xml_document doc ;
pugi::xml_parse_result ParseResult = doc.load_buffer( contents, filesize) ; // inizializzo il documento
if ( ! ParseResult) {
string sError = " Import3MF : Error parsing file. " ;
sError += ParseResult.description() ;
LOG_ERROR( GetEExLogger(), sError.c_str()) ;
return false ;
}
pugi::xml_node rootNode = doc.first_child() ;
std::string sUnit = rootNode.attribute( "unit").value() ;
if ( sUnit == "micron")
m_nScaleFactor = 0.001 ;
else if ( sUnit == "centimeter")
m_nScaleFactor = 10.0 ;
else if ( sUnit == "meter")
m_nScaleFactor = 1000.0 ;
else if ( sUnit == "inch")
m_nScaleFactor = 25.4 ;
else if ( sUnit == "foot")
m_nScaleFactor = 304.8 ;
else
m_nScaleFactor = 1.0 ; // mm
// Check sulle estensioni
string sExtensions = rootNode.attribute( "requiredextensions").value() ;
if ( sExtensions.find( "b") != string::npos)
LOG_ERROR( GetEExLogger(), " Warning : Beam Lattice Extension is not supported") ;
if ( sExtensions.find( "s") != string::npos)
LOG_ERROR( GetEExLogger(), " Warning : Slice Extension is not supported") ;
if ( sExtensions.find( "enc") != string::npos)
LOG_ERROR( GetEExLogger(), " Warning : Secure Content Extension is not supported") ;
// Analisi metadata
for ( pugi::xml_node_iterator it = rootNode.first_child() ; it != rootNode.end() ; it++){
string sName = it->name() ;
if ( sName == "metadata"){
string sValue = it->text().get() ;
m_pGDB->SetInfo( m_nIdGroup, it->attribute( "name").value(), sValue) ;
}
}
// Resource node
pugi::xml_node baseResources = rootNode.child( "resources") ;
if ( baseResources == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : no resources node") ;
return false ;
}
if ( baseResources.child( "m:texture2d") != nullptr || baseResources.child( "m:texture2dgroup") != nullptr)
LOG_ERROR( GetEExLogger(), " Warning : textures are ignored") ;
if ( baseResources.child( "m:pbspeculardisplayproperties") != nullptr || baseResources.child( "m:pbmetallicdisplayproperties") != nullptr
|| baseResources.child( "m:pbspeculartexturedisplayproperties") != nullptr || baseResources.child( "m:pbmetallictexturedisplayproperties") != nullptr
|| baseResources.child( "m:translucentdisplayproperties") != nullptr)
LOG_ERROR( GetEExLogger(), " Warning : display properties are ignored") ;
// Build node
bool bReadOk ;
pugi::xml_node build_node = rootNode.child( "build") ;
if ( build_node == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : no build node") ;
return false ;
}
// Attributes
for ( pugi::xml_attribute attr = build_node.first_attribute() ; attr ; attr = attr.next_attribute()){
if ( ! attr)
break ;
string sValue = attr.value() ;
m_pGDB->SetInfo( m_nIdGroup, attr.name(), sValue) ;
}
// Scorro tutti gli items
for ( pugi::xml_node_iterator itBuild = build_node.first_child() ; itBuild != build_node.end() ; itBuild ++){
std::string sName = itBuild->name() ;
if ( sName == "item"){
bReadOk = ReadItem( *itBuild, baseResources) ;
if ( ! bReadOk)
return false ;
}
}
return true ;
}
//---------------------------------------------------------------------------------------
string
Import3MF::FindFileName()
{
struct zip_stat sb ;
if ( zip_stat( m_ziparchive, "_rels/.rels", 0, &sb) == -1){
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening .rels file") ;
return " " ;
}
zip_file_t * file = zip_fopen( m_ziparchive, "_rels/.rels", ZIP_FL_UNCHANGED) ;
if ( file == nullptr){
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening .rels file") ;
return " " ;
}
size_t filesize = ( size_t) sb.size ;
char * contents = new char[ filesize] ;
if ( zip_fread( file, contents, sb.size) == -1){
LOG_ERROR( GetEExLogger(), " Import3MF : Error reading .rels file") ;
return " " ;
}
// Lettura file
pugi::xml_document doc ;
pugi::xml_parse_result ParseResult = doc.load_buffer( contents, filesize) ;
if ( ! ParseResult) {
string sError = " Import3MF : Error parsing file. " ;
sError += ParseResult.description() ;
LOG_ERROR( GetEExLogger(), sError.c_str()) ;
return " " ;
}
pugi::xml_node root = doc.first_child() ;
for ( pugi::xml_node_iterator it = root.first_child() ; it != root.end() ; it ++ ){
string sNodeName = it->name() ;
if ( sNodeName == "Relationship"){
if ( it->attribute( "Target") != nullptr){
string sTarget = it->attribute( "Target").value() ;
if ( sTarget.substr( 0, 11) != "/Thumbnails")
return sTarget.erase( 0, 1) ; // elimino il "/" iniziale nel nome del file
}
}
}
return " " ;
}
//----------------------------------------------------------------------------------------------------
bool
Import3MF::ReadItem( pugi::xml_node node, pugi::xml_node resources)
{
int id = node.attribute( "objectid").as_int() ;
// Trasformazione
DBLVECTOR vTransform ;
if ( node.attribute( "transform") != nullptr){
string sTransform = node.attribute( "transform").value() ;
vTransform = ReadTransform( sTransform, m_nScaleFactor) ;
if ( vTransform.empty())
return false ;
}
// Attributes
ATTRVECTOR attrItem ;
for ( pugi::xml_attribute attr = node.first_attribute() ; attr ; attr = attr.next_attribute()){
string sAttrName = attr.name() ;
string sAttrVal = attr.value() ;
if ( sAttrName != "" && sAttrVal != "" && sAttrName != "objectid" && sAttrName != "transform" && sAttrName != "p:path")
attrItem.push_back( make_pair( "Item " + sAttrName, sAttrVal)) ;
}
if ( node.child( "metadatagroup") != nullptr){
bool bReadOk = ReadMetadataGroup( node.child( "metadatagroup"), attrItem, ITEM) ;
if ( ! bReadOk)
return false ;
}
if ( node.attribute( "p:path") == nullptr ){ // elemento nello stesso file
bool bReadOk = ReadObject( resources, id, attrItem, vTransform, m_nScaleFactor) ;
if ( ! bReadOk)
return false ;
}
else { // elemento in un altro file
string sFileName = node.attribute( "p:path").value() ;
bool bReadOk = ReadFromFile( sFileName, id, attrItem, vTransform) ;
if ( ! bReadOk)
return false ;
}
return true ;
}
//-------------------------------------------------------------------------------------------------
bool
Import3MF::ReadObject( pugi::xml_node resources, int& id, ATTRVECTOR& attributes, DBLVECTOR& transform, double& nScaleFactor){
bool bFound = false ;
pugi::xml_node node ;
// Cerco nodo fra i children del nodo resources
for ( pugi::xml_node_iterator it = resources.begin() ; it != resources.end() && ! bFound ; it++){
if ( *it == nullptr)
return false ;
int nResId = it->attribute( "id").as_int() ;
if ( nResId == id){
node = *it ;
bFound = true ;
}
}
if ( ! bFound){
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : Object not found") ;
return false ;
}
// bool per capire se object contiene mesh
bool bObjMesh = false ;
if ( node.child( "mesh") != nullptr)
bObjMesh = true ;
// Attributes
int pid = -1 ;
int pindex = -1 ;
if ( node.attribute( "pid") != nullptr)
pid = node.attribute( "pid").as_int() ;
if ( node.attribute( "pindex") != nullptr)
pindex = node.attribute( "pindex").as_int() ;
// Attributes generici
for ( pugi::xml_attribute attr = node.first_attribute() ; attr ; attr = attr.next_attribute()){
string sAttrName = attr.name() ;
string sAttrVal = attr.value() ;
if ( sAttrName != "" && sAttrName != "pid" && sAttrName != "pindex" && sAttrName != "id" && sAttrVal != "" )
if ( bObjMesh)
attributes.push_back( make_pair( sAttrName, sAttrVal)) ;
else
attributes.push_back( make_pair( "Object " + sAttrName, sAttrVal)) ;
}
// Sottonodi
bool bReadOk = true ;
for ( pugi::xml_node_iterator itObj = node.first_child() ; itObj != node.end() ; itObj ++){
std::string sName = itObj->name() ;
if ( sName == "metadatagroup")
bReadOk = ReadMetadataGroup( *itObj, attributes, ( bObjMesh ? MESH : COMPONENT)) ;
else if ( sName == "mesh")
bReadOk = ReadMesh( *itObj, resources, pid, pindex, attributes, transform, nScaleFactor) ;
else if ( sName == "components")
bReadOk = ReadComponents( *itObj, resources, attributes, transform, nScaleFactor) ;
if ( ! bReadOk)
return false ;
}
return true ;
}
//----------------------------------------------------------------------------------------------------------------
bool
Import3MF::ReadMesh( pugi::xml_node node, pugi::xml_node resources, int& pid, int& pindex, ATTRVECTOR& attributes, DBLVECTOR& transform,
double& nScaleFactor)
{
StmFromTriangleSoup Stm ;
bool bStmOk = Stm.Start() ;
if ( ! bStmOk) {
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.start error") ;
return false ;
}
// Vettore vertici
std::vector< Point3d> vVertices ;
pugi::xml_node vertNode = node.child( "vertices") ;
if ( vertNode == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : no vertices") ;
return false ;
}
for ( pugi::xml_node_iterator itVert = vertNode.first_child() ; itVert != vertNode.end() ; itVert ++ ){
std::string sName = itVert->name() ;
if ( sName == "vertex"){
Point3d pt ;
// Controllo ci siano tutti gli attributes richiesti
if ( itVert->attribute( "x") == nullptr || itVert->attribute( "y") == nullptr || itVert->attribute( "z") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing vertex coordinate") ;
return false;
}
// Applico eventuale trasformazione alle coordinate
if ( ! transform.empty()) {
DBLVECTOR x { itVert->attribute( "x").as_double(), itVert->attribute( "y").as_double(),
itVert->attribute( "z").as_double(), 1} ;
DBLVECTOR res( 3, 0.0) ;
x[0] *= nScaleFactor ;
x[1] *= nScaleFactor ;
x[2] *= nScaleFactor ;
for ( size_t i = 0 ; i < 3 ; i++) {
for ( size_t j = 0 ; j < 4 ; j++)
res[i] += x[j]*transform[4*j+i] ;
}
pt.Set( res[0], res[1], res[2]) ;
}
else {
pt.Set( itVert->attribute( "x").as_double(), itVert->attribute( "y").as_double(), itVert->attribute( "z").as_double()) ;
pt *= nScaleFactor ;
}
vVertices.push_back( pt) ;
}
}
// Lettura triangoli
pugi::xml_node trgNode = node.child( "triangles") ;
if ( trgNode == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing triangles") ;
return false ;
}
for ( pugi::xml_node_iterator itTrg = trgNode.first_child() ; itTrg != trgNode.end() ; itTrg ++){
if ( *itTrg == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing triangles") ;
return false ;
}
std::string sName = itTrg->name() ;
if ( sName == "triangle"){
if ( itTrg->attribute( "v1") == nullptr || itTrg->attribute( "v2") == nullptr || itTrg->attribute( "v3") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing triangle vertex") ;
return false ;
}
int v1 = itTrg->attribute( "v1").as_int() ;
int v2 = itTrg->attribute( "v2").as_int() ;
int v3 = itTrg->attribute( "v3").as_int() ;
bool bTAddOk = Stm.AddTriangle( vVertices[v1], vVertices[v2], vVertices[v3]) ;
if ( ! bTAddOk) {
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.AddTriangle error") ;
return false ;
}
}
}
bStmOk = Stm.End() ;
if ( ! bStmOk) {
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.end error") ;
return false ;
}
ISurfTriMesh* pSurf = Stm.GetSurf() ;
if ( pSurf == nullptr) {
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.GetSurf error") ;
return false ;
}
int nId = m_pGDB->AddGeoObj( GDB_ID_NULL, m_nIdGroup, pSurf) ;
if ( nId == GDB_ID_NULL) {
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : GDB.AddGeoOjb error") ;
return false ;
}
// Colore
if ( pid != -1 && pindex != -1) {
Color col = FindColor( resources, pid, pindex) ;
if ( ! ( col == Color( 0, 0, 0, 0)))
m_pGDB->SetMaterial( nId, col) ;
}
// Attributes
for ( size_t i = 0 ; i < attributes.size() ; i++){
if ( attributes[i].first == "name")
m_pGDB->SetName( nId, attributes[i].second) ;
else
m_pGDB->SetInfo( nId, attributes[i].first, attributes[i].second) ;
}
return true ;
}
//-----------------------------------------------------------------------------------
bool
Import3MF::ReadComponents( pugi::xml_node node, pugi::xml_node resources, ATTRVECTOR attributes, DBLVECTOR vItemTransform, double& nScaleFactor)
{
// Scorro tutte le componenti
for ( pugi::xml_node_iterator it = node.first_child() ; it != node.end() ; it++){
string sName = it->name() ;
if ( sName == "component"){
int id = it->attribute( "objectid").as_int() ;
// Attributes
for ( pugi::xml_attribute attr = it->first_attribute() ; attr ; attr = attr.next_attribute()){
string sAttrName = attr.name() ;
string sAttrVal = attr.value() ;
if ( sAttrName != "" && sAttrName != "transform" && sAttrName != "p:path" && sAttrName != "objectid" && sAttrVal != "") {
attributes.push_back( make_pair( "Component " + sAttrName, sAttrVal)) ;
}
}
// Trasformazioni
bool bCompTransf = false ;
DBLVECTOR vCompTransform ;
DBLVECTOR vResTransform( 16, 0.0) ;
if ( it->attribute( "transform") != nullptr){
bCompTransf = true ;
string sTransform = it->attribute( "transform").value() ;
vCompTransform = ReadTransform( sTransform, nScaleFactor) ;
if ( vCompTransform.empty())
return false ;
}
if ( ! vItemTransform.empty() && bCompTransf ){
// Combino le trasformazioni dell'item e del component
for ( size_t i = 0 ; i < 4 ; i++)
for ( size_t j = 0 ; j < 4 ; j++)
for ( size_t k = 0 ; k < 4 ; k++)
vResTransform[4*i+j] += vCompTransform[4*i+k]*vItemTransform[4*k+j] ;
}
else if ( bCompTransf) // Ho solo la trasformazione della component
vResTransform = vCompTransform ;
else // Ho solo la trasformazione dell'item
vResTransform = vItemTransform ;
if ( it->attribute( "p:path") == nullptr){
bool bReadOk = ReadObject( resources, id, attributes, vResTransform, nScaleFactor) ;
if ( ! bReadOk)
return false ;
}
// elemento si trova in un altro file
else {
string sFileName = it->attribute( "p:path").value() ;
bool bReadOk = ReadFromFile( sFileName, id, attributes, vResTransform) ;
if ( ! bReadOk)
return false ;
}
}
}
return true ;
}
//---------------------------------------------------------------------
DBLVECTOR
Import3MF::ReadTransform( string& sTransform, double& nScaleFactor)
{
DBLVECTOR vTransform ;
// aggiungo uno spazio alla fine della stringa per gestire correttamente anche l'ultimo valore con il while
sTransform.push_back( ' ') ;
size_t str_start = 0 ;
size_t str_end ;
while( ( str_end = sTransform.find( " ", str_start)) != std::string::npos ){
string sval = sTransform.substr( str_start, str_end - str_start) ;
if ( ! sval.empty()) // check per evitare il caso di due spazi consecutivi
vTransform.push_back( std::stod( sval)) ;
str_start = str_end + 1 ;
}
if ( vTransform.size() != 12){
LOG_ERROR( GetEExLogger(), " Error reading 3MF Transform : missing transformation component") ;
vTransform.clear() ;
return vTransform ;
}
// Aggiungo ultima colonna per renderla matrice 4x4
vTransform.insert( vTransform.begin() + 3, 0.0 ) ;
vTransform.insert( vTransform.begin() + 7, 0.0 ) ;
vTransform.insert( vTransform.begin() + 11, 0.0 ) ;
vTransform.push_back( 1.0) ;
// Scale factor
vTransform[12] *= nScaleFactor ;
vTransform[13] *= nScaleFactor ;
vTransform[14] *= nScaleFactor ;
return vTransform ;
}
//------------------------------------------------------------------------------------
Color
Import3MF::FindColor( pugi::xml_node resources, int pid, int pindex)
{
// Cerco la resources con quell'id
pugi::xml_node node ;
bool bFound = false ;
for ( pugi::xml_node_iterator it = resources.begin() ; it != resources.end() && ! bFound ; it++){
int nResId = it->attribute( "id").as_int() ;
if ( nResId == pid){
node = *it ;
bFound = true ;
}
}
if ( ! bFound){
LOG_ERROR( GetEExLogger(), " Error reading 3MF : object not found") ;
return false ;
}
std::string sName = node.name() ;
// contatore per pindex
int count = -1 ;
if ( sName == "m:colorgroup" || sName == "r:colorgroup"){
for ( pugi::xml_node_iterator itColor = node.first_child() ; itColor != node.end() ; itColor ++){
std::string sColName = itColor->name() ;
if ( sColName == "m:color" || sColName == "r:color")
count ++ ;
if ( count == pindex){
std::string sCol = itColor->attribute( "color").value() ;
return ColorConverter( sCol) ;
}
}
}
else if ( sName == "basematerials"){
for ( pugi::xml_node_iterator itBase = node.first_child() ; itBase != node.end() ; itBase ++){
std::string sBaseName = itBase->name() ;
if ( sBaseName == "base")
count ++ ;
if ( count == pindex){
std::string sCol = itBase->attribute( "displaycolor").value() ;
return ColorConverter( sCol) ;
}
}
}
else if ( sName == "m:multiproperties")
return ReadMultiPropertiesColor( node, resources, pid, pindex) ;
else if ( sName == "m:compositematerials")
return ReadCompositeMaterialColor( node, resources, pid, pindex) ;
return Color( 0, 0, 0, 0) ; // colore trasparente
}
//--------------------------------------------------------------------------------------
Color
Import3MF::ReadCompositeMaterialColor( pugi::xml_node node, pugi::xml_node resources, int pid, int pindex)
{
if ( node.attribute( "matid") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF : CompositeMaterial has no BaseMaterial") ;
return Color( 0, 0, 0, 0) ;
}
int nBaseMatId = node.attribute( "matid").as_int() ;
// matindices -> da stringa vanno messi in vettore di interi
string sMatIndices = node.attribute( "matindices").value() ;
sMatIndices.push_back( ' ') ;
vector<int> vMatIndices ;
size_t str_start = 0 ;
size_t str_end ;
while( ( str_end = sMatIndices.find( " ", str_start)) != std::string::npos ){
string sval = sMatIndices.substr( str_start, str_end - str_start) ;
if ( ! sval.empty())
vMatIndices.push_back( std::stoi( sval)) ;
str_start = str_end + 1 ;
}
int count = -1 ;
string sCompValues ;
for ( pugi::xml_node_iterator it = node.first_child() ; it != node.end() ; it++){
string sNodeName = it->name() ;
if ( sNodeName == "m:composite")
count ++ ;
if ( count == pindex){
if ( it->attribute( "values") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF : CompositeMaterial has no BaseMaterial") ;
return Color( 0, 0, 0, 0) ;
}
sCompValues = it->attribute( "values").value() ;
}
}
if ( sCompValues.empty()){
LOG_ERROR( GetEExLogger(), " Error reading 3MF : CompositeMaterial has no BaseMaterial") ;
return Color( 0, 0, 0, 0) ;
}
// sCompValues -> da stringa a vettore
vector<double> vCompValues ;
sCompValues.push_back( ' ') ;
str_start = 0 ;
while( ( str_end = sCompValues.find( " ", str_start)) != std::string::npos ){
string sval = sCompValues.substr( str_start, str_end - str_start) ;
if ( ! sval.empty())
vCompValues.push_back( std::stoi( sval)) ;
str_start = str_end + 1 ;
}
// check sui valori di vCompValues
vCompValues.resize( vMatIndices.size(), 0.0) ;
double sum = 0.0 ;
for ( size_t i = 0 ; i < vCompValues.size() ; i++)
sum += vCompValues[i] ;
if ( abs( sum - 1.0) < EPS_SMALL)
for ( size_t i = 0 ; i < vCompValues.size() ; i++)
vCompValues[i] /= sum ;
if ( abs( sum) < EPS_SMALL)
vCompValues.resize( vMatIndices.size(), 1.0) ;
vector<double> col(3, 0.0) ;
Color BaseMatColor ;
for ( size_t i = 0 ; i < vMatIndices.size() ; i++){
BaseMatColor = FindColor( resources, nBaseMatId, vMatIndices[i]) ;
if ( BaseMatColor == Color( 0, 0, 0, 0))
return BaseMatColor ;
col[0] += vCompValues[i] * BaseMatColor.GetRed() ;
col[1] += vCompValues[i] * BaseMatColor.GetGreen() ;
col[2] += vCompValues[i] * BaseMatColor.GetBlue() ;
}
return Color( col[0], col[1], col[2]) ;
}
//--------------------------------------------------------------------------------------
Color
Import3MF::ReadMultiPropertiesColor( pugi::xml_node node, pugi::xml_node resources, int pid, int pindex)
{
if ( node.attribute( "pids") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF : MultiProperties has no pids") ;
return Color( 0, 0, 0, 0) ;
}
// pids -> da stringa vanno messi in vettore di interi
string spids = node.attribute( "pids").value() ;
spids.push_back( ' ') ;
vector<int> vpids ;
size_t str_start = 0 ;
size_t str_end ;
while( ( str_end = spids.find( " ", str_start)) != std::string::npos ){
string sval = spids.substr( str_start, str_end - str_start) ;
if ( ! sval.empty())
vpids.push_back( std::stoi( sval)) ;
str_start = str_end + 1 ;
}
int new_id = -1 ; // id elemento di MultiProperties che contiene un colore
size_t new_pos = -1 ; // posizione nel vettore pids associata a new_id
int count = -1 ; // contatore per pindices
// Scorro il vettore di pids
for ( size_t j = 0 ; j < vpids.size() ; j++){
bool bFound = false ;
pugi::xml_node colorNode ;
// Cerco resource con id corrispondente al pids considerato
for ( pugi::xml_node_iterator it = resources.begin() ; it != resources.end() && ! bFound ; it++){
int nResId = it->attribute( "id").as_int() ;
if ( nResId == vpids[j]){
colorNode = *it ;
bFound = true ;
}
}
// se non trovi resource con quell'id passo al pids successivo
if ( ! bFound)
continue ;
std::string colorNodeName = colorNode.name() ;
if ( colorNodeName == "basematerial" || colorNodeName == "m:colorgroup" || colorNodeName == "r:colorgroup"){
new_id = vpids[j] ;
new_pos = j ;
break ;
}
}
// se quando ho finito i pids non ho trovato un elemento che potrebbe contenere un colore
if ( new_id == -1){
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : no object defining color") ;
return Color( 0, 0, 0, 0) ;
}
// altrimenti cerco l'elemento multi del multiproperties corrispondente al pindex
for ( pugi::xml_node_iterator itMP = node.first_child() ; itMP != node.end() ; itMP ++){
std::string sMPName = itMP->name() ;
if ( sMPName == "m:multi")
count ++ ;
if ( count == pindex){
if ( itMP->attribute( "pindices") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF : MultiProperties pindices missing") ;
return Color( 0, 0, 0, 0) ;
}
string spindices = itMP->attribute( "pindices").value() ;
spindices.push_back( ' ') ;
int new_index = -1 ;
// Estraggo dalla stringa il valore in posizione new_pos
int count2 = -1 ;
str_start = 0 ;
while( ( str_end = spindices.find( " ", str_start)) != std::string::npos ){
count2 ++ ;
if ( count2 == new_pos){
string sval = spindices.substr( str_start, str_end - str_start) ;
if ( ! sval.empty())
new_index = std::stoi( sval) ;
else
new_index = 0 ;
break ;
}
str_start = str_end + 1 ;
}
if ( new_index == -1)
new_index = 0 ;
return FindColor( resources, new_id, new_index) ;
}
}
return Color( 0, 0, 0, 0) ;
}
//---------------------------------------------------------------------------------------
Color
Import3MF::ColorConverter( std::string s)
{
Color col ;
vector<int> vCol ;
if ( s.length() != 9 && s.length() != 7){
LOG_ERROR( GetEExLogger(), " Error reading 3MF Color : undefined color") ;
return Color( 0, 0, 0, 0) ;
}
int pos = 1 ;
while ( pos != s.length()){
std::string substr = s.substr( pos, 2) ;
vCol.push_back( strtol( substr.c_str(), NULL, 16)) ;
pos +=2 ;
}
// alpha
if ( vCol.size() == 4)
vCol[3] *= 100 / 255 ;
else if ( vCol.size() == 3)
vCol.push_back( 100) ;
if ( vCol[3] == 0)
vCol[3] = 100 ; // il colore è sempre assunto come completamente opaco
return Color( &vCol[0]) ;
}
//---------------------------------------------------------------------------------------
bool
Import3MF::ReadFromFile( string sFileName, int id, ATTRVECTOR& attributes, DBLVECTOR& vTransform)
{
// E' necessario togliere "/" dal nome del file
sFileName.erase( 0, 1) ;
// Se documento non è mai stato letto prima
if ( m_AuxDocuments.find( sFileName) == m_AuxDocuments.end()){
auto res = m_AuxDocuments.emplace( sFileName, pugi::xml_document()) ;
if ( ! res.second)
return false ;
struct zip_stat sb ;
if ( zip_stat( m_ziparchive, sFileName.c_str(), 0, &sb) == -1){
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening auxiliary .model file ") ;
return false ;
}
zip_file_t * file = zip_fopen( m_ziparchive, sFileName.c_str(), ZIP_FL_UNCHANGED) ;
size_t filesize = ( size_t) sb.size ;
char * contents = new char[filesize] ;
if ( zip_fread( file, contents, sb.size) == -1){
LOG_ERROR( GetEExLogger(), " Import3MF : Error reading auxiliary .model file") ;
return false ;
}
// Lettura file
pugi::xml_parse_result ParseResult = m_AuxDocuments[ sFileName].load_buffer( contents, filesize) ; // inizializzo il documento
if ( ! ParseResult) {
string sError = " Import3MF : Error parsing file : " ;
sError += ParseResult.description() ;
LOG_ERROR( GetEExLogger(), sError.c_str()) ;
return false ;
}
}
// Lettura del documento
pugi::xml_node auxrootNode = m_AuxDocuments[ sFileName].first_child() ; // node model
std::string sUnit = auxrootNode.attribute( "unit").value() ;
double nScaleFactor ;
if ( sUnit == "micron")
nScaleFactor = 0.001 ;
else if ( sUnit == "centimeter")
nScaleFactor = 10.0 ;
else if ( sUnit == "meter")
nScaleFactor = 1000.0 ;
else if ( sUnit == "inch")
nScaleFactor = 25.4 ;
else if ( sUnit == "foot")
nScaleFactor = 304.8 ;
else
nScaleFactor = 1.0 ; // mm
// Cerco nodo resources
pugi::xml_node auxResources = auxrootNode.child( "resources") ;
if ( auxResources == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : no resources node found ") ;
return false ;
}
bool bReadOk = ReadObject( auxResources, id, attributes, vTransform, nScaleFactor) ;
if ( ! bReadOk)
return false ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool
Import3MF::ReadMetadataGroup( pugi::xml_node node, ATTRVECTOR& v, MDGCaller caller)
{
for ( pugi::xml_node_iterator it = node.begin() ; it != node.end() ; it++){
string sNodeName = it->name() ;
if ( sNodeName == "metadata"){
if ( it->attribute( "name") == nullptr){
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : metadata name missing") ;
return false ;
}
string sName ;
switch ( caller){
case ITEM :
sName = "Item " ;
break ;
case COMPONENT :
sName = "Object " ;
break ;
}
sName += it->attribute( "name").value() ;
string sValue = it->text().get() ;
v.push_back( make_pair( sName, sValue)) ;
}
}
return true ;
}