2018-01-30 08:27:10 +00:00
# include "../libslic3r.h"
# include "../Model.hpp"
2018-02-13 14:19:55 +00:00
# include "../Utils.hpp"
# include "../GCode.hpp"
# include "../slic3r/GUI/PresetBundle.hpp"
2018-01-30 08:27:10 +00:00
# include "3mf.hpp"
# include <boost/algorithm/string/classification.hpp>
# include <boost/algorithm/string/split.hpp>
2018-02-13 14:19:55 +00:00
# include <boost/algorithm/string/predicate.hpp>
2018-02-08 12:26:50 +00:00
# include <boost/filesystem/operations.hpp>
# include <boost/nowide/fstream.hpp>
2018-01-30 08:27:10 +00:00
# include <expat.h>
2018-02-13 14:33:45 +00:00
# include <Eigen/Dense>
2018-01-30 08:27:10 +00:00
# include <miniz/miniz_zip.h>
2018-04-30 13:27:01 +00:00
// VERSION NUMBERS
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
const unsigned int VERSION_3MF = 1 ;
const char * SLIC3RPE_3MF_VERSION = " slic3rpe:Version3mf " ; // definition of the metadata name saved into .model file
2018-02-08 12:26:50 +00:00
const std : : string MODEL_FOLDER = " 3D/ " ;
2018-01-30 08:27:10 +00:00
const std : : string MODEL_EXTENSION = " .model " ;
2018-02-08 12:26:50 +00:00
const std : : string MODEL_FILE = " 3D/3dmodel.model " ; // << this is the only format of the string which works with CURA
const std : : string CONTENT_TYPES_FILE = " [Content_Types].xml " ;
const std : : string RELATIONSHIPS_FILE = " _rels/.rels " ;
2018-02-27 09:49:51 +00:00
const std : : string PRINT_CONFIG_FILE = " Metadata/Slic3r_PE.config " ;
const std : : string MODEL_CONFIG_FILE = " Metadata/Slic3r_PE_model.config " ;
2018-04-27 10:56:35 +00:00
const std : : string LAYER_HEIGHTS_PROFILE_FILE = " Metadata/Slic3r_PE_layer_heights_profile.txt " ;
2018-01-30 08:27:10 +00:00
const char * MODEL_TAG = " model " ;
const char * RESOURCES_TAG = " resources " ;
const char * OBJECT_TAG = " object " ;
const char * MESH_TAG = " mesh " ;
const char * VERTICES_TAG = " vertices " ;
const char * VERTEX_TAG = " vertex " ;
const char * TRIANGLES_TAG = " triangles " ;
const char * TRIANGLE_TAG = " triangle " ;
const char * COMPONENTS_TAG = " components " ;
const char * COMPONENT_TAG = " component " ;
const char * BUILD_TAG = " build " ;
const char * ITEM_TAG = " item " ;
2018-04-30 13:27:01 +00:00
const char * METADATA_TAG = " metadata " ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
const char * CONFIG_TAG = " config " ;
const char * VOLUME_TAG = " volume " ;
2018-01-30 08:27:10 +00:00
const char * UNIT_ATTR = " unit " ;
const char * NAME_ATTR = " name " ;
const char * TYPE_ATTR = " type " ;
const char * ID_ATTR = " id " ;
const char * X_ATTR = " x " ;
const char * Y_ATTR = " y " ;
const char * Z_ATTR = " z " ;
const char * V1_ATTR = " v1 " ;
const char * V2_ATTR = " v2 " ;
const char * V3_ATTR = " v3 " ;
const char * OBJECTID_ATTR = " objectid " ;
const char * TRANSFORM_ATTR = " transform " ;
2018-02-27 09:49:51 +00:00
const char * KEY_ATTR = " key " ;
const char * VALUE_ATTR = " value " ;
const char * FIRST_TRIANGLE_ID_ATTR = " firstid " ;
const char * LAST_TRIANGLE_ID_ATTR = " lastid " ;
const char * OBJECT_TYPE = " object " ;
const char * VOLUME_TYPE = " volume " ;
2018-02-28 11:11:41 +00:00
const char * NAME_KEY = " name " ;
const char * MODIFIER_KEY = " modifier " ;
2018-01-30 08:27:10 +00:00
const unsigned int VALID_OBJECT_TYPES_COUNT = 1 ;
const char * VALID_OBJECT_TYPES [ ] =
{
" model "
} ;
const unsigned int INVALID_OBJECT_TYPES_COUNT = 4 ;
const char * INVALID_OBJECT_TYPES [ ] =
{
" solidsupport " ,
" support " ,
" surface " ,
" other "
} ;
typedef Eigen : : Matrix < float , 4 , 4 , Eigen : : RowMajor > Matrix4x4 ;
const char * get_attribute_value_charptr ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
if ( ( attributes = = nullptr ) | | ( attributes_size = = 0 ) | | ( attributes_size % 2 ! = 0 ) | | ( attribute_key = = nullptr ) )
return nullptr ;
for ( unsigned int a = 0 ; a < attributes_size ; a + = 2 )
{
if ( : : strcmp ( attributes [ a ] , attribute_key ) = = 0 )
return attributes [ a + 1 ] ;
}
return nullptr ;
}
std : : string get_attribute_value_string ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
const char * text = get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ;
return ( text ! = nullptr ) ? text : " " ;
}
float get_attribute_value_float ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
const char * text = get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ;
return ( text ! = nullptr ) ? ( float ) : : atof ( text ) : 0.0f ;
}
int get_attribute_value_int ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
const char * text = get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ;
return ( text ! = nullptr ) ? : : atoi ( text ) : 0 ;
}
Matrix4x4 get_matrix_from_string ( const std : : string & mat_str )
{
if ( mat_str . empty ( ) )
// empty string means default identity matrix
return Matrix4x4 : : Identity ( ) ;
std : : vector < std : : string > mat_elements_str ;
boost : : split ( mat_elements_str , mat_str , boost : : is_any_of ( " " ) , boost : : token_compress_on ) ;
unsigned int size = ( unsigned int ) mat_elements_str . size ( ) ;
if ( size ! = 12 )
// invalid data, return identity matrix
return Matrix4x4 : : Identity ( ) ;
Matrix4x4 ret = Matrix4x4 : : Identity ( ) ;
unsigned int i = 0 ;
// matrices are stored into 3mf files as 4x3
// we need to transpose them
for ( unsigned int c = 0 ; c < 4 ; + + c )
{
for ( unsigned int r = 0 ; r < 3 ; + + r )
{
ret ( r , c ) = ( float ) : : atof ( mat_elements_str [ i + + ] . c_str ( ) ) ;
}
}
return ret ;
}
float get_unit_factor ( const std : : string & unit )
{
const char * text = unit . c_str ( ) ;
if ( : : strcmp ( text , " micron " ) = = 0 )
return 0.001f ;
else if ( : : strcmp ( text , " centimeter " ) = = 0 )
return 10.0f ;
else if ( : : strcmp ( text , " inch " ) = = 0 )
return 25.4f ;
else if ( : : strcmp ( text , " foot " ) = = 0 )
return 304.8f ;
else if ( : : strcmp ( text , " meter " ) = = 0 )
return 1000.0f ;
else
// default "millimeters" (see specification)
return 1.0f ;
}
bool is_valid_object_type ( const std : : string & type )
{
// if the type is empty defaults to "model" (see specification)
if ( type . empty ( ) )
return true ;
for ( unsigned int i = 0 ; i < VALID_OBJECT_TYPES_COUNT ; + + i )
{
if ( : : strcmp ( type . c_str ( ) , VALID_OBJECT_TYPES [ i ] ) = = 0 )
return true ;
}
return false ;
}
namespace Slic3r {
2018-02-14 13:30:03 +00:00
// Base class with error messages management
class _3MF_Base
{
std : : vector < std : : string > m_errors ;
protected :
void add_error ( const std : : string & error ) { m_errors . push_back ( error ) ; }
void clear_errors ( ) { m_errors . clear ( ) ; }
public :
void log_errors ( )
{
for ( const std : : string & error : m_errors )
{
printf ( " %s \n " , error . c_str ( ) ) ;
}
}
} ;
class _3MF_Importer : public _3MF_Base
2018-01-30 08:27:10 +00:00
{
struct Component
{
int object_id ;
Matrix4x4 matrix ;
2018-02-27 09:49:51 +00:00
explicit Component ( int object_id )
: object_id ( object_id )
, matrix ( Matrix4x4 : : Identity ( ) )
{
}
Component ( int object_id , const Matrix4x4 & matrix )
: object_id ( object_id )
, matrix ( matrix )
{
}
2018-01-30 08:27:10 +00:00
} ;
typedef std : : vector < Component > ComponentsList ;
2018-02-27 09:49:51 +00:00
struct Geometry
2018-01-30 08:27:10 +00:00
{
2018-02-27 09:49:51 +00:00
std : : vector < float > vertices ;
std : : vector < unsigned int > triangles ;
bool empty ( )
2018-01-30 08:27:10 +00:00
{
2018-02-27 09:49:51 +00:00
return vertices . empty ( ) | | triangles . empty ( ) ;
}
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
void reset ( )
{
vertices . clear ( ) ;
triangles . clear ( ) ;
}
} ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
struct CurrentObject
{
2018-01-30 08:27:10 +00:00
int id ;
Geometry geometry ;
ModelObject * object ;
ComponentsList components ;
2018-02-27 09:49:51 +00:00
CurrentObject ( )
{
reset ( ) ;
}
void reset ( )
{
id = - 1 ;
geometry . reset ( ) ;
object = nullptr ;
components . clear ( ) ;
}
} ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
struct CurrentConfig
{
int object_id ;
int volume_id ;
2018-01-30 08:27:10 +00:00
} ;
struct Instance
{
ModelInstance * instance ;
Matrix4x4 matrix ;
2018-02-27 09:49:51 +00:00
Instance ( ModelInstance * instance , const Matrix4x4 & matrix )
: instance ( instance )
, matrix ( matrix )
{
}
} ;
struct Metadata
{
std : : string key ;
std : : string value ;
Metadata ( const std : : string & key , const std : : string & value )
: key ( key )
, value ( value )
{
}
} ;
typedef std : : vector < Metadata > MetadataList ;
struct ObjectMetadata
{
struct VolumeMetadata
{
unsigned int first_triangle_id ;
unsigned int last_triangle_id ;
MetadataList metadata ;
VolumeMetadata ( unsigned int first_triangle_id , unsigned int last_triangle_id )
: first_triangle_id ( first_triangle_id )
, last_triangle_id ( last_triangle_id )
{
}
} ;
typedef std : : vector < VolumeMetadata > VolumeMetadataList ;
MetadataList metadata ;
VolumeMetadataList volumes ;
2018-01-30 08:27:10 +00:00
} ;
typedef std : : map < int , ModelObject * > IdToModelObjectMap ;
typedef std : : map < int , ComponentsList > IdToAliasesMap ;
typedef std : : vector < Instance > InstancesList ;
2018-02-27 09:49:51 +00:00
typedef std : : map < int , ObjectMetadata > IdToMetadataMap ;
typedef std : : map < int , Geometry > IdToGeometryMap ;
2018-04-27 10:56:35 +00:00
typedef std : : map < int , std : : vector < coordf_t > > IdToLayerHeightsProfileMap ;
2018-01-30 08:27:10 +00:00
2018-04-30 13:27:01 +00:00
// Version of the 3mf file
unsigned int m_version ;
2018-01-30 08:27:10 +00:00
XML_Parser m_xml_parser ;
Model * m_model ;
float m_unit_factor ;
CurrentObject m_curr_object ;
IdToModelObjectMap m_objects ;
IdToAliasesMap m_objects_aliases ;
InstancesList m_instances ;
2018-02-27 09:49:51 +00:00
IdToGeometryMap m_geometries ;
CurrentConfig m_curr_config ;
IdToMetadataMap m_objects_metadata ;
2018-04-27 10:56:35 +00:00
IdToLayerHeightsProfileMap m_layer_heights_profiles ;
2018-04-30 13:27:01 +00:00
std : : string m_curr_metadata_name ;
std : : string m_curr_characters ;
2018-01-30 08:27:10 +00:00
public :
_3MF_Importer ( ) ;
~ _3MF_Importer ( ) ;
2018-02-13 14:19:55 +00:00
bool load_model_from_file ( const std : : string & filename , Model & model , PresetBundle & bundle ) ;
2018-01-30 08:27:10 +00:00
private :
void _destroy_xml_parser ( ) ;
void _stop_xml_parser ( ) ;
2018-02-13 14:19:55 +00:00
bool _load_model_from_file ( const std : : string & filename , Model & model , PresetBundle & bundle ) ;
bool _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2018-04-27 10:56:35 +00:00
void _extract_layer_heights_profile_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
void _extract_print_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , PresetBundle & bundle , const std : : string & archive_filename ) ;
2018-02-27 09:49:51 +00:00
bool _extract_model_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model ) ;
// handlers to parse the .model file
void _handle_start_model_xml_element ( const char * name , const char * * attributes ) ;
void _handle_end_model_xml_element ( const char * name ) ;
2018-04-30 13:27:01 +00:00
void _handle_model_xml_characters ( const XML_Char * s , int len ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
// handlers to parse the MODEL_CONFIG_FILE file
void _handle_start_config_xml_element ( const char * name , const char * * attributes ) ;
void _handle_end_config_xml_element ( const char * name ) ;
2018-01-30 08:27:10 +00:00
bool _handle_start_model ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_model ( ) ;
bool _handle_start_resources ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_resources ( ) ;
bool _handle_start_object ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_object ( ) ;
bool _handle_start_mesh ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_mesh ( ) ;
bool _handle_start_vertices ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_vertices ( ) ;
bool _handle_start_vertex ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_vertex ( ) ;
bool _handle_start_triangles ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_triangles ( ) ;
bool _handle_start_triangle ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_triangle ( ) ;
bool _handle_start_components ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_components ( ) ;
bool _handle_start_component ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_component ( ) ;
bool _handle_start_build ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_build ( ) ;
bool _handle_start_item ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_item ( ) ;
2018-04-30 13:27:01 +00:00
bool _handle_start_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_metadata ( ) ;
2018-01-30 08:27:10 +00:00
bool _create_object_instance ( int object_id , const Matrix4x4 & matrix , unsigned int recur_counter ) ;
void _apply_transform ( ModelInstance & instance , const Matrix4x4 & matrix ) ;
2018-02-27 09:49:51 +00:00
bool _handle_start_config ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config_object ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_object ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config_volume ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_volume ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_metadata ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _generate_volumes ( ModelObject & object , const Geometry & geometry , const ObjectMetadata : : VolumeMetadataList & volumes ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
// callbacks to parse the .model file
static void XMLCALL _handle_start_model_xml_element ( void * userData , const char * name , const char * * attributes ) ;
static void XMLCALL _handle_end_model_xml_element ( void * userData , const char * name ) ;
2018-04-30 13:27:01 +00:00
static void XMLCALL _handle_model_xml_characters ( void * userData , const XML_Char * s , int len ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
// callbacks to parse the MODEL_CONFIG_FILE file
static void XMLCALL _handle_start_config_xml_element ( void * userData , const char * name , const char * * attributes ) ;
static void XMLCALL _handle_end_config_xml_element ( void * userData , const char * name ) ;
} ;
2018-01-30 08:27:10 +00:00
_3MF_Importer : : _3MF_Importer ( )
2018-04-30 13:27:01 +00:00
: m_version ( 0 )
, m_xml_parser ( nullptr )
2018-01-30 08:27:10 +00:00
, m_model ( nullptr )
, m_unit_factor ( 1.0f )
2018-04-30 13:27:01 +00:00
, m_curr_metadata_name ( " " )
, m_curr_characters ( " " )
2018-01-30 08:27:10 +00:00
{
}
_3MF_Importer : : ~ _3MF_Importer ( )
{
_destroy_xml_parser ( ) ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Importer : : load_model_from_file ( const std : : string & filename , Model & model , PresetBundle & bundle )
2018-01-30 08:27:10 +00:00
{
2018-04-30 13:27:01 +00:00
m_version = 0 ;
2018-01-30 08:27:10 +00:00
m_model = & model ;
m_unit_factor = 1.0f ;
m_curr_object . reset ( ) ;
m_objects . clear ( ) ;
m_objects_aliases . clear ( ) ;
m_instances . clear ( ) ;
2018-02-27 09:49:51 +00:00
m_geometries . clear ( ) ;
m_curr_config . object_id = - 1 ;
m_curr_config . volume_id = - 1 ;
m_objects_metadata . clear ( ) ;
2018-04-27 10:56:35 +00:00
m_layer_heights_profiles . clear ( ) ;
2018-04-30 13:27:01 +00:00
m_curr_metadata_name . clear ( ) ;
m_curr_characters . clear ( ) ;
2018-02-14 13:30:03 +00:00
clear_errors ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-13 14:19:55 +00:00
return _load_model_from_file ( filename , model , bundle ) ;
2018-01-30 08:27:10 +00:00
}
void _3MF_Importer : : _destroy_xml_parser ( )
{
if ( m_xml_parser ! = nullptr )
{
XML_ParserFree ( m_xml_parser ) ;
m_xml_parser = nullptr ;
}
}
void _3MF_Importer : : _stop_xml_parser ( )
{
if ( m_xml_parser ! = nullptr )
XML_StopParser ( m_xml_parser , false ) ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Importer : : _load_model_from_file ( const std : : string & filename , Model & model , PresetBundle & bundle )
2018-01-30 08:27:10 +00:00
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
mz_bool res = mz_zip_reader_init_file ( & archive , filename . c_str ( ) , 0 ) ;
if ( res = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to open the file " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive ) ;
mz_zip_archive_file_stat stat ;
2018-04-30 13:27:01 +00:00
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
2018-01-30 08:27:10 +00:00
for ( mz_uint i = 0 ; i < num_entries ; + + i )
{
if ( mz_zip_reader_file_stat ( & archive , i , & stat ) )
{
std : : string name ( stat . m_filename ) ;
2018-02-08 12:26:50 +00:00
std : : replace ( name . begin ( ) , name . end ( ) , ' \\ ' , ' / ' ) ;
2018-01-30 08:27:10 +00:00
2018-02-13 14:19:55 +00:00
if ( boost : : algorithm : : istarts_with ( name , MODEL_FOLDER ) & & boost : : algorithm : : iends_with ( name , MODEL_EXTENSION ) )
2018-01-30 08:27:10 +00:00
{
2018-02-13 14:19:55 +00:00
// valid model name -> extract model
if ( ! _extract_model_from_archive ( archive , stat ) )
2018-01-30 08:27:10 +00:00
{
mz_zip_reader_end ( & archive ) ;
2018-02-14 13:30:03 +00:00
add_error ( " Archive does not contain a valid model " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
}
2018-04-30 13:27:01 +00:00
}
}
// we then loop again the entries to read other files stored in the archive
for ( mz_uint i = 0 ; i < num_entries ; + + i )
{
if ( mz_zip_reader_file_stat ( & archive , i , & stat ) )
{
std : : string name ( stat . m_filename ) ;
std : : replace ( name . begin ( ) , name . end ( ) , ' \\ ' , ' / ' ) ;
if ( boost : : algorithm : : iequals ( name , LAYER_HEIGHTS_PROFILE_FILE ) )
2018-04-27 10:56:35 +00:00
{
// extract slic3r lazer heights profile file
_extract_layer_heights_profile_config_from_archive ( archive , stat ) ;
}
2018-02-27 09:49:51 +00:00
else if ( boost : : algorithm : : iequals ( name , PRINT_CONFIG_FILE ) )
{
// extract slic3r print config file
2018-04-27 10:56:35 +00:00
_extract_print_config_from_archive ( archive , stat , bundle , filename ) ;
2018-02-27 09:49:51 +00:00
}
else if ( boost : : algorithm : : iequals ( name , MODEL_CONFIG_FILE ) )
2018-02-13 14:19:55 +00:00
{
2018-02-27 09:49:51 +00:00
// extract slic3r model config file
if ( ! _extract_model_config_from_archive ( archive , stat , model ) )
2018-02-13 14:19:55 +00:00
{
mz_zip_reader_end ( & archive ) ;
2018-02-27 09:49:51 +00:00
add_error ( " Archive does not contain a valid model config " ) ;
2018-02-13 14:19:55 +00:00
return false ;
}
}
2018-01-30 08:27:10 +00:00
}
}
mz_zip_reader_end ( & archive ) ;
2018-02-27 09:49:51 +00:00
for ( const IdToModelObjectMap : : value_type & object : m_objects )
{
ObjectMetadata : : VolumeMetadataList volumes ;
ObjectMetadata : : VolumeMetadataList * volumes_ptr = nullptr ;
IdToGeometryMap : : const_iterator obj_geometry = m_geometries . find ( object . first ) ;
if ( obj_geometry = = m_geometries . end ( ) )
{
add_error ( " Unable to find object geometry " ) ;
return false ;
}
2018-04-27 10:56:35 +00:00
IdToLayerHeightsProfileMap : : iterator obj_layer_heights_profile = m_layer_heights_profiles . find ( object . first ) ;
if ( obj_layer_heights_profile ! = m_layer_heights_profiles . end ( ) )
{
object . second - > layer_height_profile = obj_layer_heights_profile - > second ;
object . second - > layer_height_profile_valid = true ;
}
2018-02-27 09:49:51 +00:00
IdToMetadataMap : : iterator obj_metadata = m_objects_metadata . find ( object . first ) ;
if ( obj_metadata ! = m_objects_metadata . end ( ) )
{
// config data has been found, this model was saved using slic3r pe
2018-02-27 14:46:54 +00:00
// apply object's name and config data
2018-02-27 09:49:51 +00:00
for ( const Metadata & metadata : obj_metadata - > second . metadata )
{
2018-02-27 14:46:54 +00:00
if ( metadata . key = = " name " )
object . second - > name = metadata . value ;
else
object . second - > config . set_deserialize ( metadata . key , metadata . value ) ;
2018-02-27 09:49:51 +00:00
}
// select object's detected volumes
volumes_ptr = & obj_metadata - > second . volumes ;
}
else
{
// config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate
volumes . emplace_back ( 0 , ( int ) obj_geometry - > second . triangles . size ( ) / 3 - 1 ) ;
// select as volumes
volumes_ptr = & volumes ;
}
if ( ! _generate_volumes ( * object . second , obj_geometry - > second , * volumes_ptr ) )
return false ;
}
2018-03-06 09:26:39 +00:00
// fixes the min z of the model if negative
model . adjust_min_z ( ) ;
2018-01-30 08:27:10 +00:00
return true ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Importer : : _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
2018-01-30 08:27:10 +00:00
{
2018-02-14 13:30:03 +00:00
if ( stat . m_uncomp_size = = 0 )
{
add_error ( " Found invalid size " ) ;
return false ;
}
2018-01-30 08:27:10 +00:00
_destroy_xml_parser ( ) ;
m_xml_parser = XML_ParserCreate ( nullptr ) ;
if ( m_xml_parser = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to create parser " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
XML_SetUserData ( m_xml_parser , ( void * ) this ) ;
2018-02-27 09:49:51 +00:00
XML_SetElementHandler ( m_xml_parser , _3MF_Importer : : _handle_start_model_xml_element , _3MF_Importer : : _handle_end_model_xml_element ) ;
2018-04-30 13:27:01 +00:00
XML_SetCharacterDataHandler ( m_xml_parser , _3MF_Importer : : _handle_model_xml_characters ) ;
2018-01-30 08:27:10 +00:00
void * parser_buffer = XML_GetBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size ) ;
if ( parser_buffer = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to create buffer " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , parser_buffer , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " Error while reading model data to buffer " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
if ( ! XML_ParseBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size , 1 ) )
{
char error_buf [ 1024 ] ;
: : sprintf ( error_buf , " Error (%s) while parsing xml file at line %d " , XML_ErrorString ( XML_GetErrorCode ( m_xml_parser ) ) , XML_GetCurrentLineNumber ( m_xml_parser ) ) ;
2018-02-14 13:30:03 +00:00
add_error ( error_buf ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
return true ;
}
2018-04-27 10:56:35 +00:00
void _3MF_Importer : : _extract_print_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , PresetBundle & bundle , const std : : string & archive_filename )
2018-02-13 14:19:55 +00:00
{
if ( stat . m_uncomp_size > 0 )
{
2018-04-27 10:56:35 +00:00
std : : string buffer ( ( size_t ) stat . m_uncomp_size , 0 ) ;
2018-02-13 14:19:55 +00:00
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , ( void * ) buffer . data ( ) , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " Error while reading config data to buffer " ) ;
2018-04-27 10:56:35 +00:00
return ;
2018-02-13 14:19:55 +00:00
}
bundle . load_config_string ( buffer . data ( ) , archive_filename . c_str ( ) ) ;
}
2018-04-27 10:56:35 +00:00
}
2018-02-13 14:19:55 +00:00
2018-04-27 10:56:35 +00:00
void _3MF_Importer : : _extract_layer_heights_profile_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
{
if ( stat . m_uncomp_size > 0 )
{
std : : string buffer ( ( size_t ) stat . m_uncomp_size , 0 ) ;
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , ( void * ) buffer . data ( ) , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
add_error ( " Error while reading layer heights profile data to buffer " ) ;
return ;
}
if ( buffer . back ( ) = = ' \n ' )
buffer . pop_back ( ) ;
std : : vector < std : : string > objects ;
boost : : split ( objects , buffer , boost : : is_any_of ( " \n " ) , boost : : token_compress_off ) ;
for ( const std : : string & object : objects )
{
std : : vector < std : : string > object_data ;
boost : : split ( object_data , object , boost : : is_any_of ( " | " ) , boost : : token_compress_off ) ;
if ( object_data . size ( ) ! = 2 )
{
add_error ( " Error while reading object data " ) ;
continue ;
}
std : : vector < std : : string > object_data_id ;
boost : : split ( object_data_id , object_data [ 0 ] , boost : : is_any_of ( " = " ) , boost : : token_compress_off ) ;
if ( object_data_id . size ( ) ! = 2 )
{
add_error ( " Error while reading object id " ) ;
continue ;
}
int object_id = std : : atoi ( object_data_id [ 1 ] . c_str ( ) ) ;
if ( object_id = = 0 )
{
add_error ( " Found invalid object id " ) ;
continue ;
}
IdToLayerHeightsProfileMap : : iterator object_item = m_layer_heights_profiles . find ( object_id ) ;
if ( object_item ! = m_layer_heights_profiles . end ( ) )
{
add_error ( " Found duplicated layer heights profile " ) ;
continue ;
}
std : : vector < std : : string > object_data_profile ;
boost : : split ( object_data_profile , object_data [ 1 ] , boost : : is_any_of ( " ; " ) , boost : : token_compress_off ) ;
if ( ( object_data_profile . size ( ) < = 4 ) | | ( object_data_profile . size ( ) % 2 ! = 0 ) )
{
add_error ( " Found invalid layer heights profile " ) ;
continue ;
}
std : : vector < coordf_t > profile ;
profile . reserve ( object_data_profile . size ( ) ) ;
for ( const std : : string & value : object_data_profile )
{
profile . push_back ( ( coordf_t ) std : : atof ( value . c_str ( ) ) ) ;
}
m_layer_heights_profiles . insert ( IdToLayerHeightsProfileMap : : value_type ( object_id , profile ) ) ;
}
}
2018-02-13 14:19:55 +00:00
}
2018-02-27 09:49:51 +00:00
bool _3MF_Importer : : _extract_model_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model )
{
if ( stat . m_uncomp_size = = 0 )
{
add_error ( " Found invalid size " ) ;
return false ;
}
_destroy_xml_parser ( ) ;
m_xml_parser = XML_ParserCreate ( nullptr ) ;
if ( m_xml_parser = = nullptr )
{
add_error ( " Unable to create parser " ) ;
return false ;
}
XML_SetUserData ( m_xml_parser , ( void * ) this ) ;
XML_SetElementHandler ( m_xml_parser , _3MF_Importer : : _handle_start_config_xml_element , _3MF_Importer : : _handle_end_config_xml_element ) ;
void * parser_buffer = XML_GetBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size ) ;
if ( parser_buffer = = nullptr )
{
add_error ( " Unable to create buffer " ) ;
return false ;
}
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , parser_buffer , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
add_error ( " Error while reading config data to buffer " ) ;
return false ;
}
if ( ! XML_ParseBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size , 1 ) )
{
char error_buf [ 1024 ] ;
: : sprintf ( error_buf , " Error (%s) while parsing xml file at line %d " , XML_ErrorString ( XML_GetErrorCode ( m_xml_parser ) ) , XML_GetCurrentLineNumber ( m_xml_parser ) ) ;
add_error ( error_buf ) ;
return false ;
}
return true ;
}
void _3MF_Importer : : _handle_start_model_xml_element ( const char * name , const char * * attributes )
2018-01-30 08:27:10 +00:00
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
unsigned int num_attributes = ( unsigned int ) XML_GetSpecifiedAttributeCount ( m_xml_parser ) ;
if ( : : strcmp ( MODEL_TAG , name ) = = 0 )
res = _handle_start_model ( attributes , num_attributes ) ;
else if ( : : strcmp ( RESOURCES_TAG , name ) = = 0 )
res = _handle_start_resources ( attributes , num_attributes ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_start_object ( attributes , num_attributes ) ;
else if ( : : strcmp ( MESH_TAG , name ) = = 0 )
res = _handle_start_mesh ( attributes , num_attributes ) ;
else if ( : : strcmp ( VERTICES_TAG , name ) = = 0 )
res = _handle_start_vertices ( attributes , num_attributes ) ;
else if ( : : strcmp ( VERTEX_TAG , name ) = = 0 )
res = _handle_start_vertex ( attributes , num_attributes ) ;
else if ( : : strcmp ( TRIANGLES_TAG , name ) = = 0 )
res = _handle_start_triangles ( attributes , num_attributes ) ;
else if ( : : strcmp ( TRIANGLE_TAG , name ) = = 0 )
res = _handle_start_triangle ( attributes , num_attributes ) ;
else if ( : : strcmp ( COMPONENTS_TAG , name ) = = 0 )
res = _handle_start_components ( attributes , num_attributes ) ;
else if ( : : strcmp ( COMPONENT_TAG , name ) = = 0 )
res = _handle_start_component ( attributes , num_attributes ) ;
else if ( : : strcmp ( BUILD_TAG , name ) = = 0 )
res = _handle_start_build ( attributes , num_attributes ) ;
else if ( : : strcmp ( ITEM_TAG , name ) = = 0 )
res = _handle_start_item ( attributes , num_attributes ) ;
2018-04-30 13:27:01 +00:00
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_start_metadata ( attributes , num_attributes ) ;
2018-01-30 08:27:10 +00:00
if ( ! res )
_stop_xml_parser ( ) ;
}
2018-02-27 09:49:51 +00:00
void _3MF_Importer : : _handle_end_model_xml_element ( const char * name )
2018-01-30 08:27:10 +00:00
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
if ( : : strcmp ( MODEL_TAG , name ) = = 0 )
res = _handle_end_model ( ) ;
else if ( : : strcmp ( RESOURCES_TAG , name ) = = 0 )
res = _handle_end_resources ( ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_end_object ( ) ;
else if ( : : strcmp ( MESH_TAG , name ) = = 0 )
res = _handle_end_mesh ( ) ;
else if ( : : strcmp ( VERTICES_TAG , name ) = = 0 )
res = _handle_end_vertices ( ) ;
else if ( : : strcmp ( VERTEX_TAG , name ) = = 0 )
res = _handle_end_vertex ( ) ;
else if ( : : strcmp ( TRIANGLES_TAG , name ) = = 0 )
res = _handle_end_triangles ( ) ;
else if ( : : strcmp ( TRIANGLE_TAG , name ) = = 0 )
res = _handle_end_triangle ( ) ;
else if ( : : strcmp ( COMPONENTS_TAG , name ) = = 0 )
res = _handle_end_components ( ) ;
else if ( : : strcmp ( COMPONENT_TAG , name ) = = 0 )
res = _handle_end_component ( ) ;
else if ( : : strcmp ( BUILD_TAG , name ) = = 0 )
res = _handle_end_build ( ) ;
else if ( : : strcmp ( ITEM_TAG , name ) = = 0 )
res = _handle_end_item ( ) ;
2018-04-30 13:27:01 +00:00
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_end_metadata ( ) ;
2018-01-30 08:27:10 +00:00
if ( ! res )
_stop_xml_parser ( ) ;
}
2018-04-30 13:27:01 +00:00
void _3MF_Importer : : _handle_model_xml_characters ( const XML_Char * s , int len )
{
m_curr_characters . append ( s , len ) ;
}
2018-02-27 09:49:51 +00:00
void _3MF_Importer : : _handle_start_config_xml_element ( const char * name , const char * * attributes )
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
unsigned int num_attributes = ( unsigned int ) XML_GetSpecifiedAttributeCount ( m_xml_parser ) ;
if ( : : strcmp ( CONFIG_TAG , name ) = = 0 )
res = _handle_start_config ( attributes , num_attributes ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_start_config_object ( attributes , num_attributes ) ;
else if ( : : strcmp ( VOLUME_TAG , name ) = = 0 )
res = _handle_start_config_volume ( attributes , num_attributes ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_start_config_metadata ( attributes , num_attributes ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
void _3MF_Importer : : _handle_end_config_xml_element ( const char * name )
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
if ( : : strcmp ( CONFIG_TAG , name ) = = 0 )
res = _handle_end_config ( ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_end_config_object ( ) ;
else if ( : : strcmp ( VOLUME_TAG , name ) = = 0 )
res = _handle_end_config_volume ( ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_end_config_metadata ( ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
2018-01-30 08:27:10 +00:00
bool _3MF_Importer : : _handle_start_model ( const char * * attributes , unsigned int num_attributes )
{
m_unit_factor = get_unit_factor ( get_attribute_value_string ( attributes , num_attributes , UNIT_ATTR ) ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_model ( )
{
// deletes all non-built or non-instanced objects
for ( const IdToModelObjectMap : : value_type & object : m_objects )
{
if ( ( object . second ! = nullptr ) & & ( object . second - > instances . size ( ) = = 0 ) )
m_model - > delete_object ( object . second ) ;
}
// applies instances' matrices
for ( Instance & instance : m_instances )
{
if ( instance . instance ! = nullptr )
{
ModelObject * object = instance . instance - > get_object ( ) ;
2018-04-12 08:10:15 +00:00
if ( object ! = nullptr )
2018-01-30 08:27:10 +00:00
{
2018-04-12 08:10:15 +00:00
// apply the matrix to the instance
2018-02-27 09:49:51 +00:00
_apply_transform ( * instance . instance , instance . matrix ) ;
2018-01-30 08:27:10 +00:00
}
}
}
return true ;
}
bool _3MF_Importer : : _handle_start_resources ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_end_resources ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_object ( const char * * attributes , unsigned int num_attributes )
{
// reset current data
m_curr_object . reset ( ) ;
if ( is_valid_object_type ( get_attribute_value_string ( attributes , num_attributes , TYPE_ATTR ) ) )
{
// create new object (it may be removed later if no instances are generated from it)
m_curr_object . object = m_model - > add_object ( ) ;
if ( m_curr_object . object = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to create object " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
// set object data
m_curr_object . object - > name = get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
m_curr_object . id = get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
}
return true ;
}
bool _3MF_Importer : : _handle_end_object ( )
{
if ( m_curr_object . object ! = nullptr )
{
if ( m_curr_object . geometry . empty ( ) )
{
// no geometry defined
// remove the object from the model
m_model - > delete_object ( m_curr_object . object ) ;
if ( m_curr_object . components . empty ( ) )
{
// no components defined -> invalid object, delete it
IdToModelObjectMap : : iterator object_item = m_objects . find ( m_curr_object . id ) ;
if ( object_item ! = m_objects . end ( ) )
m_objects . erase ( object_item ) ;
IdToAliasesMap : : iterator alias_item = m_objects_aliases . find ( m_curr_object . id ) ;
if ( alias_item ! = m_objects_aliases . end ( ) )
m_objects_aliases . erase ( alias_item ) ;
}
else
// adds components to aliases
m_objects_aliases . insert ( IdToAliasesMap : : value_type ( m_curr_object . id , m_curr_object . components ) ) ;
}
else
{
2018-02-27 09:49:51 +00:00
// geometry defined, store it for later use
m_geometries . insert ( IdToGeometryMap : : value_type ( m_curr_object . id , std : : move ( m_curr_object . geometry ) ) ) ;
2018-01-30 08:27:10 +00:00
// stores the object for later use
if ( m_objects . find ( m_curr_object . id ) = = m_objects . end ( ) )
{
m_objects . insert ( IdToModelObjectMap : : value_type ( m_curr_object . id , m_curr_object . object ) ) ;
m_objects_aliases . insert ( IdToAliasesMap : : value_type ( m_curr_object . id , ComponentsList ( 1 , Component ( m_curr_object . id ) ) ) ) ; // aliases itself
}
else
{
2018-02-14 13:30:03 +00:00
add_error ( " Found object with duplicate id " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
}
}
return true ;
}
bool _3MF_Importer : : _handle_start_mesh ( const char * * attributes , unsigned int num_attributes )
{
// reset current geometry
m_curr_object . geometry . reset ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_mesh ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_vertices ( const char * * attributes , unsigned int num_attributes )
{
// reset current vertices
m_curr_object . geometry . vertices . clear ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_vertices ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_vertex ( const char * * attributes , unsigned int num_attributes )
{
// appends the vertex coordinates
// missing values are set equal to ZERO
m_curr_object . geometry . vertices . push_back ( m_unit_factor * get_attribute_value_float ( attributes , num_attributes , X_ATTR ) ) ;
m_curr_object . geometry . vertices . push_back ( m_unit_factor * get_attribute_value_float ( attributes , num_attributes , Y_ATTR ) ) ;
m_curr_object . geometry . vertices . push_back ( m_unit_factor * get_attribute_value_float ( attributes , num_attributes , Z_ATTR ) ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_vertex ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_triangles ( const char * * attributes , unsigned int num_attributes )
{
// reset current triangles
m_curr_object . geometry . triangles . clear ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_triangles ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_triangle ( const char * * attributes , unsigned int num_attributes )
{
// we are ignoring the following attributes:
// p1
// p2
// p3
// pid
// see specifications
// appends the triangle's vertices indices
// missing values are set equal to ZERO
m_curr_object . geometry . triangles . push_back ( ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , V1_ATTR ) ) ;
m_curr_object . geometry . triangles . push_back ( ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , V2_ATTR ) ) ;
m_curr_object . geometry . triangles . push_back ( ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , V3_ATTR ) ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_triangle ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_components ( const char * * attributes , unsigned int num_attributes )
{
// reset current components
m_curr_object . components . clear ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_components ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_component ( const char * * attributes , unsigned int num_attributes )
{
int object_id = get_attribute_value_int ( attributes , num_attributes , OBJECTID_ATTR ) ;
Matrix4x4 matrix = get_matrix_from_string ( get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
IdToModelObjectMap : : iterator object_item = m_objects . find ( object_id ) ;
if ( object_item = = m_objects . end ( ) )
{
IdToAliasesMap : : iterator alias_item = m_objects_aliases . find ( object_id ) ;
if ( alias_item = = m_objects_aliases . end ( ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Found component with invalid object id " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
}
m_curr_object . components . emplace_back ( object_id , matrix ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_component ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_build ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_end_build ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_item ( const char * * attributes , unsigned int num_attributes )
{
// we are ignoring the following attributes
// thumbnail
// partnumber
// pid
// pindex
// see specifications
int object_id = get_attribute_value_int ( attributes , num_attributes , OBJECTID_ATTR ) ;
Matrix4x4 matrix = get_matrix_from_string ( get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
return _create_object_instance ( object_id , matrix , 1 ) ;
}
bool _3MF_Importer : : _handle_end_item ( )
{
// do nothing
return true ;
}
2018-04-30 13:27:01 +00:00
bool _3MF_Importer : : _handle_start_metadata ( const char * * attributes , unsigned int num_attributes )
{
m_curr_characters . clear ( ) ;
std : : string name = get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
if ( ! name . empty ( ) )
m_curr_metadata_name = name ;
return true ;
}
bool _3MF_Importer : : _handle_end_metadata ( )
{
if ( m_curr_metadata_name = = SLIC3RPE_3MF_VERSION )
m_version = ( unsigned int ) atoi ( m_curr_characters . c_str ( ) ) ;
return true ;
}
2018-01-30 08:27:10 +00:00
bool _3MF_Importer : : _create_object_instance ( int object_id , const Matrix4x4 & matrix , unsigned int recur_counter )
{
static const unsigned int MAX_RECURSIONS = 10 ;
// escape from circular aliasing
if ( recur_counter > MAX_RECURSIONS )
{
2018-02-14 13:30:03 +00:00
add_error ( " Too many recursions " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
IdToAliasesMap : : iterator it = m_objects_aliases . find ( object_id ) ;
if ( it = = m_objects_aliases . end ( ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Found item with invalid object id " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
if ( ( it - > second . size ( ) = = 1 ) & & ( it - > second [ 0 ] . object_id = = object_id ) )
{
// aliasing to itself
IdToModelObjectMap : : iterator object_item = m_objects . find ( object_id ) ;
if ( ( object_item = = m_objects . end ( ) ) | | ( object_item - > second = = nullptr ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Found invalid object " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
else
{
ModelInstance * instance = object_item - > second - > add_instance ( ) ;
if ( instance = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add object instance " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
m_instances . emplace_back ( instance , matrix ) ;
}
}
else
{
// recursively process nested components
for ( const Component & component : it - > second )
{
if ( ! _create_object_instance ( component . object_id , matrix * component . matrix , recur_counter + 1 ) )
return false ;
}
}
return true ;
}
void _3MF_Importer : : _apply_transform ( ModelInstance & instance , const Matrix4x4 & matrix )
{
// slic3r ModelInstance cannot be transformed using a matrix
// we extract from the given matrix only the values currently used
// translation
double offset_x = ( double ) matrix ( 0 , 3 ) ;
double offset_y = ( double ) matrix ( 1 , 3 ) ;
double offset_z = ( double ) matrix ( 2 , 3 ) ;
// scale
double sx = : : sqrt ( sqr ( ( double ) matrix ( 0 , 0 ) ) + sqr ( ( double ) matrix ( 1 , 0 ) ) + sqr ( ( double ) matrix ( 2 , 0 ) ) ) ;
double sy = : : sqrt ( sqr ( ( double ) matrix ( 0 , 1 ) ) + sqr ( ( double ) matrix ( 1 , 1 ) ) + sqr ( ( double ) matrix ( 2 , 1 ) ) ) ;
double sz = : : sqrt ( sqr ( ( double ) matrix ( 0 , 2 ) ) + sqr ( ( double ) matrix ( 1 , 2 ) ) + sqr ( ( double ) matrix ( 2 , 2 ) ) ) ;
// invalid scale value, return
if ( ( sx = = 0.0 ) | | ( sy = = 0.0 ) | | ( sz = = 0.0 ) )
return ;
// non-uniform scale value, return
if ( ( std : : abs ( sx - sy ) > 0.00001 ) | | ( std : : abs ( sx - sz ) > 0.00001 ) )
return ;
// rotations (extracted using quaternion)
double inv_sx = 1.0 / sx ;
double inv_sy = 1.0 / sy ;
double inv_sz = 1.0 / sz ;
Eigen : : Matrix < double , 3 , 3 , Eigen : : RowMajor > m3x3 ;
m3x3 < < ( double ) matrix ( 0 , 0 ) * inv_sx , ( double ) matrix ( 0 , 1 ) * inv_sy , ( double ) matrix ( 0 , 2 ) * inv_sz ,
( double ) matrix ( 1 , 0 ) * inv_sx , ( double ) matrix ( 1 , 1 ) * inv_sy , ( double ) matrix ( 1 , 2 ) * inv_sz ,
( double ) matrix ( 2 , 0 ) * inv_sx , ( double ) matrix ( 2 , 1 ) * inv_sy , ( double ) matrix ( 2 , 2 ) * inv_sz ;
double qw = 0.5 * : : sqrt ( std : : max ( 0.0 , 1.0 + m3x3 ( 0 , 0 ) + m3x3 ( 1 , 1 ) + m3x3 ( 2 , 2 ) ) ) ;
double qx = 0.5 * : : sqrt ( std : : max ( 0.0 , 1.0 + m3x3 ( 0 , 0 ) - m3x3 ( 1 , 1 ) - m3x3 ( 2 , 2 ) ) ) ;
double qy = 0.5 * : : sqrt ( std : : max ( 0.0 , 1.0 - m3x3 ( 0 , 0 ) + m3x3 ( 1 , 1 ) - m3x3 ( 2 , 2 ) ) ) ;
double qz = 0.5 * : : sqrt ( std : : max ( 0.0 , 1.0 - m3x3 ( 0 , 0 ) - m3x3 ( 1 , 1 ) + m3x3 ( 2 , 2 ) ) ) ;
double q_magnitude = : : sqrt ( sqr ( qw ) + sqr ( qx ) + sqr ( qy ) + sqr ( qz ) ) ;
// invalid length, return
if ( q_magnitude = = 0.0 )
return ;
double inv_q_magnitude = 1.0 / q_magnitude ;
qw * = inv_q_magnitude ;
qx * = inv_q_magnitude ;
qy * = inv_q_magnitude ;
qz * = inv_q_magnitude ;
double test = qx * qy + qz * qw ;
double angle_x , angle_y , angle_z ;
if ( test > 0.499 )
{
// singularity at north pole
angle_x = 0.0 ;
angle_y = 2.0 * : : atan2 ( qx , qw ) ;
angle_z = 0.5 * PI ;
}
else if ( test < - 0.499 )
{
// singularity at south pole
angle_x = 0.0 ;
angle_y = - 2.0 * : : atan2 ( qx , qw ) ;
angle_z = - 0.5 * PI ;
}
else
{
angle_x = : : atan2 ( 2.0 * qx * qw - 2.0 * qy * qz , 1.0 - 2.0 * sqr ( qx ) - 2.0 * sqr ( qz ) ) ;
angle_y = : : atan2 ( 2.0 * qy * qw - 2.0 * qx * qz , 1.0 - 2.0 * sqr ( qy ) - 2.0 * sqr ( qz ) ) ;
angle_z = : : asin ( 2.0 * qx * qy + 2.0 * qz * qw ) ;
if ( angle_x < 0.0 )
angle_x + = 2.0 * PI ;
if ( angle_y < 0.0 )
angle_y + = 2.0 * PI ;
if ( angle_z < 0.0 )
angle_z + = 2.0 * PI ;
}
instance . offset . x = offset_x ;
instance . offset . y = offset_y ;
instance . scaling_factor = sx ;
instance . rotation = angle_z ;
}
2018-02-27 09:49:51 +00:00
bool _3MF_Importer : : _handle_start_config ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_end_config ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_config_object ( const char * * attributes , unsigned int num_attributes )
{
int object_id = get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
IdToMetadataMap : : iterator object_item = m_objects_metadata . find ( object_id ) ;
if ( object_item ! = m_objects_metadata . end ( ) )
{
add_error ( " Found duplicated object id " ) ;
return false ;
}
m_objects_metadata . insert ( IdToMetadataMap : : value_type ( object_id , ObjectMetadata ( ) ) ) ;
m_curr_config . object_id = object_id ;
return true ;
}
bool _3MF_Importer : : _handle_end_config_object ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_config_volume ( const char * * attributes , unsigned int num_attributes )
{
IdToMetadataMap : : iterator object = m_objects_metadata . find ( m_curr_config . object_id ) ;
if ( object = = m_objects_metadata . end ( ) )
{
add_error ( " Cannot assign volume to a valid object " ) ;
return false ;
}
m_curr_config . volume_id = object - > second . volumes . size ( ) ;
unsigned int first_triangle_id = ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , FIRST_TRIANGLE_ID_ATTR ) ;
unsigned int last_triangle_id = ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , LAST_TRIANGLE_ID_ATTR ) ;
object - > second . volumes . emplace_back ( first_triangle_id , last_triangle_id ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_config_volume ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_config_metadata ( const char * * attributes , unsigned int num_attributes )
{
IdToMetadataMap : : iterator object = m_objects_metadata . find ( m_curr_config . object_id ) ;
if ( object = = m_objects_metadata . end ( ) )
{
add_error ( " Cannot assign metadata to valid object id " ) ;
return false ;
}
std : : string type = get_attribute_value_string ( attributes , num_attributes , TYPE_ATTR ) ;
std : : string key = get_attribute_value_string ( attributes , num_attributes , KEY_ATTR ) ;
std : : string value = get_attribute_value_string ( attributes , num_attributes , VALUE_ATTR ) ;
if ( type = = OBJECT_TYPE )
object - > second . metadata . emplace_back ( key , value ) ;
else if ( type = = VOLUME_TYPE )
{
if ( m_curr_config . volume_id < object - > second . volumes . size ( ) )
object - > second . volumes [ m_curr_config . volume_id ] . metadata . emplace_back ( key , value ) ;
}
else
{
add_error ( " Found invalid metadata type " ) ;
return false ;
}
return true ;
}
bool _3MF_Importer : : _handle_end_config_metadata ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _generate_volumes ( ModelObject & object , const Geometry & geometry , const ObjectMetadata : : VolumeMetadataList & volumes )
{
if ( ! object . volumes . empty ( ) )
{
add_error ( " Found invalid volumes count " ) ;
return false ;
}
unsigned int geo_tri_count = geometry . triangles . size ( ) / 3 ;
for ( const ObjectMetadata : : VolumeMetadata & volume_data : volumes )
{
if ( ( geo_tri_count < = volume_data . first_triangle_id ) | | ( geo_tri_count < = volume_data . last_triangle_id ) | | ( volume_data . last_triangle_id < volume_data . first_triangle_id ) )
{
add_error ( " Found invalid triangle id " ) ;
return false ;
}
// splits volume out of imported geometry
unsigned int triangles_count = volume_data . last_triangle_id - volume_data . first_triangle_id + 1 ;
ModelVolume * volume = object . add_volume ( TriangleMesh ( ) ) ;
stl_file & stl = volume - > mesh . stl ;
stl . stats . type = inmemory ;
stl . stats . number_of_facets = ( uint32_t ) triangles_count ;
stl . stats . original_num_facets = ( int ) stl . stats . number_of_facets ;
stl_allocate ( & stl ) ;
unsigned int src_start_id = volume_data . first_triangle_id * 3 ;
for ( size_t i = 0 ; i < triangles_count ; + + i )
{
unsigned int ii = i * 3 ;
stl_facet & facet = stl . facet_start [ i ] ;
for ( unsigned int v = 0 ; v < 3 ; + + v )
{
: : memcpy ( ( void * ) & facet . vertex [ v ] . x , ( const void * ) & geometry . vertices [ geometry . triangles [ src_start_id + ii + v ] * 3 ] , 3 * sizeof ( float ) ) ;
}
}
stl_get_size ( & stl ) ;
volume - > mesh . repair ( ) ;
2018-02-27 14:46:54 +00:00
// apply volume's name and config data
2018-02-27 09:49:51 +00:00
for ( const Metadata & metadata : volume_data . metadata )
{
2018-02-28 11:11:41 +00:00
if ( metadata . key = = NAME_KEY )
2018-02-27 14:46:54 +00:00
volume - > name = metadata . value ;
2018-02-28 11:11:41 +00:00
else if ( ( metadata . key = = MODIFIER_KEY ) & & ( metadata . value = = " 1 " ) )
volume - > modifier = true ;
2018-02-27 14:46:54 +00:00
else
volume - > config . set_deserialize ( metadata . key , metadata . value ) ;
2018-02-27 09:49:51 +00:00
}
}
return true ;
}
void XMLCALL _3MF_Importer : : _handle_start_model_xml_element ( void * userData , const char * name , const char * * attributes )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_model_xml_element ( name , attributes ) ;
}
void XMLCALL _3MF_Importer : : _handle_end_model_xml_element ( void * userData , const char * name )
2018-01-30 08:27:10 +00:00
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
2018-02-27 09:49:51 +00:00
importer - > _handle_end_model_xml_element ( name ) ;
2018-01-30 08:27:10 +00:00
}
2018-04-30 13:27:01 +00:00
void XMLCALL _3MF_Importer : : _handle_model_xml_characters ( void * userData , const XML_Char * s , int len )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_model_xml_characters ( s , len ) ;
}
2018-02-27 09:49:51 +00:00
void XMLCALL _3MF_Importer : : _handle_start_config_xml_element ( void * userData , const char * name , const char * * attributes )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_config_xml_element ( name , attributes ) ;
}
void XMLCALL _3MF_Importer : : _handle_end_config_xml_element ( void * userData , const char * name )
2018-01-30 08:27:10 +00:00
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
2018-02-27 09:49:51 +00:00
importer - > _handle_end_config_xml_element ( name ) ;
2018-01-30 08:27:10 +00:00
}
2018-02-14 13:30:03 +00:00
class _3MF_Exporter : public _3MF_Base
2018-02-08 12:26:50 +00:00
{
struct BuildItem
{
unsigned int id ;
Matrix4x4 matrix ;
2018-02-27 09:49:51 +00:00
BuildItem ( unsigned int id , const Matrix4x4 & matrix )
: id ( id )
, matrix ( matrix )
{
}
} ;
struct Offsets
{
unsigned int first_vertex_id ;
unsigned int first_triangle_id ;
unsigned int last_triangle_id ;
Offsets ( unsigned int first_vertex_id )
: first_vertex_id ( first_vertex_id )
, first_triangle_id ( - 1 )
, last_triangle_id ( - 1 )
{
}
} ;
typedef std : : map < const ModelVolume * , Offsets > VolumeToOffsetsMap ;
struct ObjectData
{
ModelObject * object ;
VolumeToOffsetsMap volumes_offsets ;
explicit ObjectData ( ModelObject * object )
: object ( object )
{
}
2018-02-08 12:26:50 +00:00
} ;
typedef std : : vector < BuildItem > BuildItemsList ;
2018-02-27 09:49:51 +00:00
typedef std : : map < int , ObjectData > IdToObjectDataMap ;
IdToObjectDataMap m_objects_data ;
2018-02-08 12:26:50 +00:00
public :
2018-03-22 12:49:48 +00:00
bool save_model_to_file ( const std : : string & filename , Model & model , const Print & print , bool export_print_config ) ;
2018-02-08 12:26:50 +00:00
private :
2018-03-22 12:49:48 +00:00
bool _save_model_to_file ( const std : : string & filename , Model & model , const Print & print , bool export_print_config ) ;
2018-02-13 14:19:55 +00:00
bool _add_content_types_file_to_archive ( mz_zip_archive & archive ) ;
bool _add_relationships_file_to_archive ( mz_zip_archive & archive ) ;
bool _add_model_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
2018-02-27 09:49:51 +00:00
bool _add_object_to_model_stream ( std : : stringstream & stream , unsigned int & object_id , ModelObject & object , BuildItemsList & build_items , VolumeToOffsetsMap & volumes_offsets ) ;
bool _add_mesh_to_object_stream ( std : : stringstream & stream , ModelObject & object , VolumeToOffsetsMap & volumes_offsets ) ;
2018-02-08 12:26:50 +00:00
bool _add_build_to_model_stream ( std : : stringstream & stream , const BuildItemsList & build_items ) ;
2018-04-27 10:56:35 +00:00
bool _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
2018-02-27 09:49:51 +00:00
bool _add_print_config_file_to_archive ( mz_zip_archive & archive , const Print & print ) ;
bool _add_model_config_file_to_archive ( mz_zip_archive & archive , const Model & model ) ;
2018-02-08 12:26:50 +00:00
} ;
2018-03-22 12:49:48 +00:00
bool _3MF_Exporter : : save_model_to_file ( const std : : string & filename , Model & model , const Print & print , bool export_print_config )
2018-02-08 12:26:50 +00:00
{
2018-02-14 13:30:03 +00:00
clear_errors ( ) ;
2018-03-22 12:49:48 +00:00
return _save_model_to_file ( filename , model , print , export_print_config ) ;
2018-02-08 12:26:50 +00:00
}
2018-03-22 12:49:48 +00:00
bool _3MF_Exporter : : _save_model_to_file ( const std : : string & filename , Model & model , const Print & print , bool export_print_config )
2018-02-08 12:26:50 +00:00
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
2018-02-27 09:49:51 +00:00
m_objects_data . clear ( ) ;
2018-02-08 12:26:50 +00:00
mz_bool res = mz_zip_writer_init_file ( & archive , filename . c_str ( ) , 0 ) ;
if ( res = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to open the file " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
// adds content types file
2018-02-13 14:19:55 +00:00
if ( ! _add_content_types_file_to_archive ( archive ) )
2018-02-08 12:26:50 +00:00
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
// adds relationships file
2018-02-13 14:19:55 +00:00
if ( ! _add_relationships_file_to_archive ( archive ) )
2018-02-08 12:26:50 +00:00
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
// adds model file
2018-02-13 14:19:55 +00:00
if ( ! _add_model_file_to_archive ( archive , model ) )
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2018-04-27 10:56:35 +00:00
// adds layer height profile file
if ( ! _add_layer_height_profile_file_to_archive ( archive , model ) )
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2018-02-27 09:49:51 +00:00
// adds slic3r print config file
2018-03-22 12:49:48 +00:00
if ( export_print_config )
2018-02-27 09:49:51 +00:00
{
2018-03-22 12:49:48 +00:00
if ( ! _add_print_config_file_to_archive ( archive , print ) )
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2018-02-27 09:49:51 +00:00
}
// adds slic3r model config file
if ( ! _add_model_config_file_to_archive ( archive , model ) )
2018-02-08 12:26:50 +00:00
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
if ( ! mz_zip_writer_finalize_archive ( & archive ) )
{
mz_zip_writer_end ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
2018-02-14 13:30:03 +00:00
add_error ( " Unable to finalize the archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
mz_zip_writer_end ( & archive ) ;
return true ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Exporter : : _add_content_types_file_to_archive ( mz_zip_archive & archive )
2018-02-08 12:26:50 +00:00
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <Types xmlns= \" http://schemas.openxmlformats.org/package/2006/content-types \" > \n " ;
stream < < " <Default Extension= \" rels \" ContentType= \" application/vnd.openxmlformats-package.relationships+xml \" /> \n " ;
stream < < " <Default Extension= \" model \" ContentType= \" application/vnd.ms-package.3dmanufacturing-3dmodel+xml \" /> \n " ;
stream < < " </Types> " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , CONTENT_TYPES_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add content types file to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
return true ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Exporter : : _add_relationships_file_to_archive ( mz_zip_archive & archive )
2018-02-08 12:26:50 +00:00
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <Relationships xmlns= \" http://schemas.openxmlformats.org/package/2006/relationships \" > \n " ;
stream < < " <Relationship Target= \" / " < < MODEL_FILE < < " \" Id= \" rel-1 \" Type= \" http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel \" /> \n " ;
stream < < " </Relationships> " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , RELATIONSHIPS_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add relationships file to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
return true ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Exporter : : _add_model_file_to_archive ( mz_zip_archive & archive , Model & model )
2018-02-08 12:26:50 +00:00
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
2018-04-30 13:27:01 +00:00
stream < < " < " < < MODEL_TAG < < " unit= \" millimeter \" xml:lang= \" en-US \" xmlns= \" http://schemas.microsoft.com/3dmanufacturing/core/2015/02 \" xmlns:slic3rpe= \" http://schemas.slic3r.org/3mf/2017/06 \" > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" " < < SLIC3RPE_3MF_VERSION < < " \" > " < < VERSION_3MF < < " </ " < < METADATA_TAG < < " > \n " ;
2018-02-27 09:49:51 +00:00
stream < < " < " < < RESOURCES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
BuildItemsList build_items ;
unsigned int object_id = 1 ;
for ( ModelObject * obj : model . objects )
{
if ( obj = = nullptr )
continue ;
2018-02-27 09:49:51 +00:00
unsigned int curr_id = object_id ;
IdToObjectDataMap : : iterator object_it = m_objects_data . insert ( IdToObjectDataMap : : value_type ( curr_id , ObjectData ( obj ) ) ) . first ;
if ( ! _add_object_to_model_stream ( stream , object_id , * obj , build_items , object_it - > second . volumes_offsets ) )
2018-02-08 12:26:50 +00:00
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add object to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < RESOURCES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
if ( ! _add_build_to_model_stream ( stream , build_items ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add build to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < MODEL_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , MODEL_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add model file to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
return true ;
}
2018-02-27 09:49:51 +00:00
bool _3MF_Exporter : : _add_object_to_model_stream ( std : : stringstream & stream , unsigned int & object_id , ModelObject & object , BuildItemsList & build_items , VolumeToOffsetsMap & volumes_offsets )
2018-02-08 12:26:50 +00:00
{
unsigned int id = 0 ;
for ( const ModelInstance * instance : object . instances )
{
if ( instance = = nullptr )
continue ;
unsigned int instance_id = object_id + id ;
2018-02-27 09:49:51 +00:00
stream < < " < " < < OBJECT_TAG < < " id= \" " < < instance_id < < " \" type= \" model \" > \n " ;
2018-02-08 12:26:50 +00:00
if ( id = = 0 )
{
2018-02-27 09:49:51 +00:00
if ( ! _add_mesh_to_object_stream ( stream , object , volumes_offsets ) )
2018-02-08 12:26:50 +00:00
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add mesh to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
}
else
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < COMPONENTS_TAG < < " > \n " ;
stream < < " < " < < COMPONENT_TAG < < " objectid= \" " < < object_id < < " \" /> \n " ;
stream < < " </ " < < COMPONENTS_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
}
Eigen : : Affine3f transform ;
2018-04-12 08:10:15 +00:00
transform = Eigen : : Translation3f ( ( float ) instance - > offset . x , ( float ) instance - > offset . y , 0.0f ) * Eigen : : AngleAxisf ( ( float ) instance - > rotation , Eigen : : Vector3f : : UnitZ ( ) ) * Eigen : : Scaling ( ( float ) instance - > scaling_factor ) ;
2018-02-08 12:26:50 +00:00
build_items . emplace_back ( instance_id , transform . matrix ( ) ) ;
2018-02-27 09:49:51 +00:00
stream < < " </ " < < OBJECT_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
+ + id ;
}
object_id + = id ;
return true ;
}
2018-02-27 09:49:51 +00:00
bool _3MF_Exporter : : _add_mesh_to_object_stream ( std : : stringstream & stream , ModelObject & object , VolumeToOffsetsMap & volumes_offsets )
2018-02-08 12:26:50 +00:00
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < MESH_TAG < < " > \n " ;
stream < < " < " < < VERTICES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
unsigned int vertices_count = 0 ;
for ( ModelVolume * volume : object . volumes )
{
if ( volume = = nullptr )
continue ;
2018-02-27 09:49:51 +00:00
VolumeToOffsetsMap : : iterator volume_it = volumes_offsets . insert ( VolumeToOffsetsMap : : value_type ( volume , Offsets ( vertices_count ) ) ) . first ;
2018-02-08 12:26:50 +00:00
if ( ! volume - > mesh . repaired )
volume - > mesh . repair ( ) ;
stl_file & stl = volume - > mesh . stl ;
if ( stl . v_shared = = nullptr )
stl_generate_shared_vertices ( & stl ) ;
if ( stl . stats . shared_vertices = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " Found invalid mesh " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
vertices_count + = stl . stats . shared_vertices ;
for ( int i = 0 ; i < stl . stats . shared_vertices ; + + i )
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < VERTEX_TAG < < " " ;
2018-04-12 08:10:15 +00:00
stream < < " x= \" " < < stl . v_shared [ i ] . x < < " \" " ;
stream < < " y= \" " < < stl . v_shared [ i ] . y < < " \" " ;
stream < < " z= \" " < < stl . v_shared [ i ] . z < < " \" /> \n " ;
2018-02-08 12:26:50 +00:00
}
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < VERTICES_TAG < < " > \n " ;
stream < < " < " < < TRIANGLES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
2018-02-27 09:49:51 +00:00
unsigned int triangles_count = 0 ;
2018-02-08 12:26:50 +00:00
for ( ModelVolume * volume : object . volumes )
{
if ( volume = = nullptr )
continue ;
2018-02-27 09:49:51 +00:00
VolumeToOffsetsMap : : iterator volume_it = volumes_offsets . find ( volume ) ;
assert ( volume_it ! = volumes_offsets . end ( ) ) ;
2018-02-08 12:26:50 +00:00
stl_file & stl = volume - > mesh . stl ;
2018-02-27 09:49:51 +00:00
// updates triangle offsets
volume_it - > second . first_triangle_id = triangles_count ;
triangles_count + = stl . stats . number_of_facets ;
volume_it - > second . last_triangle_id = triangles_count - 1 ;
2018-02-08 12:26:50 +00:00
for ( uint32_t i = 0 ; i < stl . stats . number_of_facets ; + + i )
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < TRIANGLE_TAG < < " " ;
2018-02-08 12:26:50 +00:00
for ( int j = 0 ; j < 3 ; + + j )
{
2018-02-27 09:49:51 +00:00
stream < < " v " < < j + 1 < < " = \" " < < stl . v_indices [ i ] . vertex [ j ] + volume_it - > second . first_vertex_id < < " \" " ;
2018-02-08 12:26:50 +00:00
}
stream < < " /> \n " ;
}
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < TRIANGLES_TAG < < " > \n " ;
stream < < " </ " < < MESH_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
return true ;
}
bool _3MF_Exporter : : _add_build_to_model_stream ( std : : stringstream & stream , const BuildItemsList & build_items )
{
if ( build_items . size ( ) = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " No build item found " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2018-02-27 09:49:51 +00:00
stream < < " < " < < BUILD_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
for ( const BuildItem & item : build_items )
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < ITEM_TAG < < " objectid= \" " < < item . id < < " \" transform = \" " ;
2018-02-08 12:26:50 +00:00
for ( unsigned c = 0 ; c < 4 ; + + c )
{
for ( unsigned r = 0 ; r < 3 ; + + r )
{
stream < < item . matrix ( r , c ) ;
if ( ( r ! = 2 ) | | ( c ! = 3 ) )
stream < < " " ;
}
}
stream < < " \" /> \n " ;
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < BUILD_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
return true ;
}
2018-04-27 10:56:35 +00:00
bool _3MF_Exporter : : _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model )
{
std : : string out = " " ;
char buffer [ 1024 ] ;
unsigned int count = 0 ;
for ( const ModelObject * object : model . objects )
{
+ + count ;
std : : vector < double > layer_height_profile = object - > layer_height_profile_valid ? object - > layer_height_profile : std : : vector < double > ( ) ;
if ( ( layer_height_profile . size ( ) > = 4 ) & & ( ( layer_height_profile . size ( ) % 2 ) = = 0 ) )
{
sprintf ( buffer , " object_id=%d| " , count ) ;
out + = buffer ;
// Store the layer height profile as a single semicolon separated list.
for ( size_t i = 0 ; i < layer_height_profile . size ( ) ; + + i )
{
sprintf ( buffer , ( i = = 0 ) ? " %f " : " ;%f " , layer_height_profile [ i ] ) ;
out + = buffer ;
}
out + = " \n " ;
}
}
if ( ! out . empty ( ) )
{
if ( ! mz_zip_writer_add_mem ( & archive , LAYER_HEIGHTS_PROFILE_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
add_error ( " Unable to add layer heights profile file to archive " ) ;
return false ;
}
}
return true ;
}
2018-02-27 09:49:51 +00:00
bool _3MF_Exporter : : _add_print_config_file_to_archive ( mz_zip_archive & archive , const Print & print )
2018-02-13 14:19:55 +00:00
{
char buffer [ 1024 ] ;
sprintf ( buffer , " ; %s \n \n " , header_slic3r_generated ( ) . c_str ( ) ) ;
std : : string out = buffer ;
GCode : : append_full_config ( print , out ) ;
2018-04-27 10:56:35 +00:00
if ( ! out . empty ( ) )
2018-02-13 14:19:55 +00:00
{
2018-04-27 10:56:35 +00:00
if ( ! mz_zip_writer_add_mem ( & archive , PRINT_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
add_error ( " Unable to add print config file to archive " ) ;
return false ;
}
2018-02-27 09:49:51 +00:00
}
return true ;
}
bool _3MF_Exporter : : _add_model_config_file_to_archive ( mz_zip_archive & archive , const Model & model )
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " < " < < CONFIG_TAG < < " > \n " ;
for ( const IdToObjectDataMap : : value_type & obj_metadata : m_objects_data )
{
const ModelObject * obj = obj_metadata . second . object ;
if ( obj ! = nullptr )
{
stream < < " < " < < OBJECT_TAG < < " id= \" " < < obj_metadata . first < < " \" > \n " ;
2018-02-27 14:46:54 +00:00
// stores object's name
if ( ! obj - > name . empty ( ) )
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < OBJECT_TYPE < < " \" " < < KEY_ATTR < < " = \" name \" " < < VALUE_ATTR < < " = \" " < < obj - > name < < " \" /> \n " ;
2018-02-27 09:49:51 +00:00
// stores object's config data
for ( const std : : string & key : obj - > config . keys ( ) )
{
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < OBJECT_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < key < < " \" " < < VALUE_ATTR < < " = \" " < < obj - > config . serialize ( key ) < < " \" /> \n " ;
}
for ( const ModelVolume * volume : obj_metadata . second . object - > volumes )
{
if ( volume ! = nullptr )
{
const VolumeToOffsetsMap & offsets = obj_metadata . second . volumes_offsets ;
VolumeToOffsetsMap : : const_iterator it = offsets . find ( volume ) ;
if ( it ! = offsets . end ( ) )
{
// stores volume's offsets
stream < < " < " < < VOLUME_TAG < < " " ;
stream < < FIRST_TRIANGLE_ID_ATTR < < " = \" " < < it - > second . first_triangle_id < < " \" " ;
stream < < LAST_TRIANGLE_ID_ATTR < < " = \" " < < it - > second . last_triangle_id < < " \" > \n " ;
2018-02-27 14:46:54 +00:00
// stores volume's name
if ( ! volume - > name . empty ( ) )
2018-02-28 11:11:41 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < NAME_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > name < < " \" /> \n " ;
// stores volume's modifier field
if ( volume - > modifier )
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < MODIFIER_KEY < < " \" " < < VALUE_ATTR < < " = \" 1 \" /> \n " ;
2018-02-27 14:46:54 +00:00
2018-02-27 09:49:51 +00:00
// stores volume's config data
for ( const std : : string & key : volume - > config . keys ( ) )
{
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < key < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > config . serialize ( key ) < < " \" /> \n " ;
}
2018-02-27 14:46:54 +00:00
2018-02-27 09:49:51 +00:00
stream < < " </ " < < VOLUME_TAG < < " > \n " ;
}
}
}
stream < < " </ " < < OBJECT_TAG < < " > \n " ;
}
}
stream < < " </ " < < CONFIG_TAG < < " > \n " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , MODEL_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
add_error ( " Unable to add model config file to archive " ) ;
2018-02-13 14:19:55 +00:00
return false ;
}
return true ;
}
bool load_3mf ( const char * path , PresetBundle * bundle , Model * model )
2018-01-30 08:27:10 +00:00
{
2018-02-13 14:19:55 +00:00
if ( ( path = = nullptr ) | | ( bundle = = nullptr ) | | ( model = = nullptr ) )
2018-01-30 08:27:10 +00:00
return false ;
_3MF_Importer importer ;
2018-02-13 14:19:55 +00:00
bool res = importer . load_model_from_file ( path , * model , * bundle ) ;
2018-04-27 10:56:35 +00:00
importer . log_errors ( ) ;
2018-01-30 08:27:10 +00:00
return res ;
}
2018-02-08 12:26:50 +00:00
2018-03-22 12:49:48 +00:00
bool store_3mf ( const char * path , Model * model , Print * print , bool export_print_config )
2018-02-08 12:26:50 +00:00
{
2018-02-13 14:19:55 +00:00
if ( ( path = = nullptr ) | | ( model = = nullptr ) | | ( print = = nullptr ) )
2018-02-08 12:26:50 +00:00
return false ;
_3MF_Exporter exporter ;
2018-03-22 12:49:48 +00:00
bool res = exporter . save_model_to_file ( path , * model , * print , export_print_config ) ;
2018-02-08 12:26:50 +00:00
if ( ! res )
2018-02-14 13:30:03 +00:00
exporter . log_errors ( ) ;
2018-02-08 12:26:50 +00:00
return res ;
}
2018-01-30 08:27:10 +00:00
} // namespace Slic3r