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