2018-01-30 09:27:10 +01:00
#include "../libslic3r.h"
#include "../Model.hpp"
2018-02-13 15:19:55 +01:00
#include "../Utils.hpp"
#include "../GCode.hpp"
#include "../slic3r/GUI/PresetBundle.hpp"
2018-01-30 09:27:10 +01:00
#include "3mf.hpp"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
2018-02-13 15:19:55 +01:00
#include <boost/algorithm/string/predicate.hpp>
2018-02-08 13:26:50 +01:00
#include <boost/filesystem/operations.hpp>
#include <boost/nowide/fstream.hpp>
2018-01-30 09:27:10 +01:00
#include <expat.h>
2018-02-13 15:33:45 +01:00
#include <Eigen/Dense>
2018-01-30 09:27:10 +01:00
#include <miniz/miniz_zip.h>
2018-04-30 15:27:01 +02: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 13:26:50 +01:00
const std :: string MODEL_FOLDER = "3D/" ;
2018-01-30 09:27:10 +01:00
const std :: string MODEL_EXTENSION = ".model" ;
2018-02-08 13:26:50 +01: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 10:49:51 +01: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 12:56:35 +02:00
const std :: string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt" ;
2018-01-30 09:27:10 +01: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 15:27:01 +02:00
const char * METADATA_TAG = "metadata" ;
2018-01-30 09:27:10 +01:00
2018-02-27 10:49:51 +01:00
const char * CONFIG_TAG = "config" ;
const char * VOLUME_TAG = "volume" ;
2018-01-30 09:27:10 +01: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 10:49:51 +01: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 12:11:41 +01:00
const char * NAME_KEY = "name" ;
const char * MODIFIER_KEY = "modifier" ;
2018-01-30 09:27:10 +01: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 14:30:03 +01: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 09:27:10 +01:00
{
struct Component
{
int object_id ;
Matrix4x4 matrix ;
2018-02-27 10:49:51 +01: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 09:27:10 +01:00
};
typedef std :: vector < Component > ComponentsList ;
2018-02-27 10:49:51 +01:00
struct Geometry
{
std :: vector < float > vertices ;
std :: vector < unsigned int > triangles ;
bool empty ()
{
return vertices . empty () || triangles . empty ();
}
void reset ()
{
vertices . clear ();
triangles . clear ();
}
};
2018-01-30 09:27:10 +01:00
struct CurrentObject
{
int id ;
Geometry geometry ;
ModelObject * object ;
ComponentsList components ;
2018-02-27 10:49:51 +01:00
CurrentObject ()
{
reset ();
}
2018-01-30 09:27:10 +01:00
2018-02-27 10:49:51 +01:00
void reset ()
{
id = - 1 ;
geometry . reset ();
object = nullptr ;
components . clear ();
}
};
struct CurrentConfig
{
int object_id ;
int volume_id ;
2018-01-30 09:27:10 +01:00
};
struct Instance
{
ModelInstance * instance ;
Matrix4x4 matrix ;
2018-02-27 10:49:51 +01: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 09:27:10 +01:00
};
typedef std :: map < int , ModelObject *> IdToModelObjectMap ;
typedef std :: map < int , ComponentsList > IdToAliasesMap ;
typedef std :: vector < Instance > InstancesList ;
2018-02-27 10:49:51 +01:00
typedef std :: map < int , ObjectMetadata > IdToMetadataMap ;
typedef std :: map < int , Geometry > IdToGeometryMap ;
2018-04-27 12:56:35 +02:00
typedef std :: map < int , std :: vector < coordf_t >> IdToLayerHeightsProfileMap ;
2018-01-30 09:27:10 +01:00
2018-04-30 15:27:01 +02:00
// Version of the 3mf file
unsigned int m_version ;
2018-01-30 09:27:10 +01: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 10:49:51 +01:00
IdToGeometryMap m_geometries ;
CurrentConfig m_curr_config ;
IdToMetadataMap m_objects_metadata ;
2018-04-27 12:56:35 +02:00
IdToLayerHeightsProfileMap m_layer_heights_profiles ;
2018-04-30 15:27:01 +02:00
std :: string m_curr_metadata_name ;
std :: string m_curr_characters ;
2018-01-30 09:27:10 +01:00
public :
_3MF_Importer ();
~ _3MF_Importer ();
2018-02-13 15:19:55 +01:00
bool load_model_from_file ( const std :: string & filename , Model & model , PresetBundle & bundle );
2018-01-30 09:27:10 +01:00
private :
void _destroy_xml_parser ();
void _stop_xml_parser ();
2018-02-13 15:19:55 +01: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 12:56:35 +02: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 10:49:51 +01:00
bool _extract_model_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model );
2018-01-30 09:27:10 +01:00
2018-02-27 10:49:51 +01:00
// 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 15:27:01 +02:00
void _handle_model_xml_characters ( const XML_Char * s , int len );
2018-02-27 10:49:51 +01: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 09:27:10 +01: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 15:27:01 +02:00
bool _handle_start_metadata ( const char ** attributes , unsigned int num_attributes );
bool _handle_end_metadata ();
2018-01-30 09:27:10 +01: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 10:49:51 +01:00
bool _handle_start_config ( const char ** attributes , unsigned int num_attributes );
bool _handle_end_config ();
bool _handle_start_config_object ( const char ** attributes , unsigned int num_attributes );
bool _handle_end_config_object ();
bool _handle_start_config_volume ( const char ** attributes , unsigned int num_attributes );
bool _handle_end_config_volume ();
bool _handle_start_config_metadata ( const char ** attributes , unsigned int num_attributes );
bool _handle_end_config_metadata ();
bool _generate_volumes ( ModelObject & object , const Geometry & geometry , const ObjectMetadata :: VolumeMetadataList & volumes );
// 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 15:27:01 +02:00
static void XMLCALL _handle_model_xml_characters ( void * userData , const XML_Char * s , int len );
2018-02-27 10:49:51 +01: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 09:27:10 +01:00
};
_3MF_Importer :: _3MF_Importer ()
2018-04-30 15:27:01 +02:00
: m_version ( 0 )
, m_xml_parser ( nullptr )
2018-01-30 09:27:10 +01:00
, m_model ( nullptr )
, m_unit_factor ( 1.0f )
2018-04-30 15:27:01 +02:00
, m_curr_metadata_name ( "" )
, m_curr_characters ( "" )
2018-01-30 09:27:10 +01:00
{
}
_3MF_Importer ::~ _3MF_Importer ()
{
_destroy_xml_parser ();
}
2018-02-13 15:19:55 +01:00
bool _3MF_Importer :: load_model_from_file ( const std :: string & filename , Model & model , PresetBundle & bundle )
2018-01-30 09:27:10 +01:00
{
2018-04-30 15:27:01 +02:00
m_version = 0 ;
2018-01-30 09:27:10 +01: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 10:49:51 +01:00
m_geometries . clear ();
m_curr_config . object_id = - 1 ;
m_curr_config . volume_id = - 1 ;
m_objects_metadata . clear ();
2018-04-27 12:56:35 +02:00
m_layer_heights_profiles . clear ();
2018-04-30 15:27:01 +02:00
m_curr_metadata_name . clear ();
m_curr_characters . clear ();
2018-02-14 14:30:03 +01:00
clear_errors ();
2018-01-30 09:27:10 +01:00
2018-02-13 15:19:55 +01:00
return _load_model_from_file ( filename , model , bundle );
2018-01-30 09:27:10 +01: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 15:19:55 +01:00
bool _3MF_Importer :: _load_model_from_file ( const std :: string & filename , Model & model , PresetBundle & bundle )
2018-01-30 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Unable to open the file" );
2018-01-30 09:27:10 +01:00
return false ;
}
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive );
mz_zip_archive_file_stat stat ;
2018-04-30 15:27:01 +02: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 09:27:10 +01: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 13:26:50 +01:00
std :: replace ( name . begin (), name . end (), '\\' , '/' );
2018-01-30 09:27:10 +01:00
2018-02-13 15:19:55 +01:00
if ( boost :: algorithm :: istarts_with ( name , MODEL_FOLDER ) && boost :: algorithm :: iends_with ( name , MODEL_EXTENSION ))
2018-01-30 09:27:10 +01:00
{
2018-02-13 15:19:55 +01:00
// valid model name -> extract model
if ( ! _extract_model_from_archive ( archive , stat ))
2018-01-30 09:27:10 +01:00
{
mz_zip_reader_end ( & archive );
2018-02-14 14:30:03 +01:00
add_error ( "Archive does not contain a valid model" );
2018-01-30 09:27:10 +01:00
return false ;
}
}
2018-04-30 15:27:01 +02: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 12:56:35 +02:00
{
// extract slic3r lazer heights profile file
_extract_layer_heights_profile_config_from_archive ( archive , stat );
}
2018-02-27 10:49:51 +01:00
else if ( boost :: algorithm :: iequals ( name , PRINT_CONFIG_FILE ))
2018-02-13 15:19:55 +01:00
{
2018-02-27 10:49:51 +01:00
// extract slic3r print config file
2018-04-27 12:56:35 +02:00
_extract_print_config_from_archive ( archive , stat , bundle , filename );
2018-02-27 10:49:51 +01:00
}
else if ( boost :: algorithm :: iequals ( name , MODEL_CONFIG_FILE ))
{
// extract slic3r model config file
if ( ! _extract_model_config_from_archive ( archive , stat , model ))
{
mz_zip_reader_end ( & archive );
add_error ( "Archive does not contain a valid model config" );
2018-02-13 15:19:55 +01:00
return false ;
}
}
2018-01-30 09:27:10 +01:00
}
}
mz_zip_reader_end ( & archive );
2018-02-27 10:49:51 +01: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 12:56:35 +02: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 10:49:51 +01: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 15:46:54 +01:00
// apply object's name and config data
2018-02-27 10:49:51 +01:00
for ( const Metadata & metadata : obj_metadata -> second . metadata )
{
2018-02-27 15:46:54 +01:00
if ( metadata . key == "name" )
object . second -> name = metadata . value ;
else
object . second -> config . set_deserialize ( metadata . key , metadata . value );
2018-02-27 10:49:51 +01: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 10:26:39 +01:00
// fixes the min z of the model if negative
model . adjust_min_z ();
2018-01-30 09:27:10 +01:00
return true ;
}
2018-02-13 15:19:55 +01:00
bool _3MF_Importer :: _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
2018-01-30 09:27:10 +01:00
{
2018-02-14 14:30:03 +01:00
if ( stat . m_uncomp_size == 0 )
{
add_error ( "Found invalid size" );
return false ;
}
2018-01-30 09:27:10 +01:00
_destroy_xml_parser ();
m_xml_parser = XML_ParserCreate ( nullptr );
if ( m_xml_parser == nullptr )
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to create parser" );
2018-01-30 09:27:10 +01:00
return false ;
}
XML_SetUserData ( m_xml_parser , ( void * ) this );
2018-02-27 10:49:51 +01:00
XML_SetElementHandler ( m_xml_parser , _3MF_Importer :: _handle_start_model_xml_element , _3MF_Importer :: _handle_end_model_xml_element );
2018-04-30 15:27:01 +02:00
XML_SetCharacterDataHandler ( m_xml_parser , _3MF_Importer :: _handle_model_xml_characters );
2018-01-30 09:27:10 +01:00
void * parser_buffer = XML_GetBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size );
if ( parser_buffer == nullptr )
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to create buffer" );
2018-01-30 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Error while reading model data to buffer" );
2018-01-30 09:27:10 +01: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 14:30:03 +01:00
add_error ( error_buf );
2018-01-30 09:27:10 +01:00
return false ;
}
return true ;
}
2018-04-27 12:56:35 +02: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 15:19:55 +01:00
{
if ( stat . m_uncomp_size > 0 )
{
2018-04-27 12:56:35 +02:00
std :: string buffer (( size_t ) stat . m_uncomp_size , 0 );
2018-02-13 15:19:55 +01: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 14:30:03 +01:00
add_error ( "Error while reading config data to buffer" );
2018-04-27 12:56:35 +02:00
return ;
2018-02-13 15:19:55 +01:00
}
bundle . load_config_string ( buffer . data (), archive_filename . c_str ());
}
2018-04-27 12:56:35 +02:00
}
2018-02-13 15:19:55 +01:00
2018-04-27 12:56:35 +02: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 15:19:55 +01:00
}
2018-02-27 10:49:51 +01: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 09:27:10 +01: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 15:27:01 +02:00
else if ( :: strcmp ( METADATA_TAG , name ) == 0 )
res = _handle_start_metadata ( attributes , num_attributes );
2018-01-30 09:27:10 +01:00
if ( ! res )
_stop_xml_parser ();
}
2018-02-27 10:49:51 +01:00
void _3MF_Importer :: _handle_end_model_xml_element ( const char * name )
2018-01-30 09:27:10 +01: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 15:27:01 +02:00
else if ( :: strcmp ( METADATA_TAG , name ) == 0 )
res = _handle_end_metadata ();
2018-01-30 09:27:10 +01:00
if ( ! res )
_stop_xml_parser ();
}
2018-04-30 15:27:01 +02:00
void _3MF_Importer :: _handle_model_xml_characters ( const XML_Char * s , int len )
{
m_curr_characters . append ( s , len );
}
2018-02-27 10:49:51 +01: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 09:27:10 +01: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 10:10:15 +02:00
if ( object != nullptr )
2018-01-30 09:27:10 +01:00
{
2018-04-12 10:10:15 +02:00
// apply the matrix to the instance
2018-02-27 10:49:51 +01:00
_apply_transform ( * instance . instance , instance . matrix );
2018-01-30 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Unable to create object" );
2018-01-30 09:27:10 +01: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 10:49:51 +01: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 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Found object with duplicate id" );
2018-01-30 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Found component with invalid object id" );
2018-01-30 09:27:10 +01: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 15:27:01 +02: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 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Too many recursions" );
2018-01-30 09:27:10 +01:00
return false ;
}
IdToAliasesMap :: iterator it = m_objects_aliases . find ( object_id );
if ( it == m_objects_aliases . end ())
{
2018-02-14 14:30:03 +01:00
add_error ( "Found item with invalid object id" );
2018-01-30 09:27:10 +01: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 14:30:03 +01:00
add_error ( "Found invalid object" );
2018-01-30 09:27:10 +01:00
return false ;
}
else
{
ModelInstance * instance = object_item -> second -> add_instance ();
if ( instance == nullptr )
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to add object instance" );
2018-01-30 09:27:10 +01: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 ;
2018-06-07 16:13:32 +02:00
#if 0 // use quaternions
2018-01-30 09:27:10 +01:00
// 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;
}
2018-06-07 16:13:32 +02:00
#else // use eigen library
double inv_sx = 1.0 / sx ;
double inv_sy = 1.0 / sy ;
double inv_sz = 1.0 / sz ;
Eigen :: Matrix3d 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 ;
Eigen :: AngleAxisd rotation ;
rotation . fromRotationMatrix ( m3x3 );
// invalid rotation axis, we currently handle only rotations around Z axis
if (( rotation . angle () != 0.0 ) && ( rotation . axis () != Eigen :: Vector3d :: UnitZ ()) && ( rotation . axis () != - Eigen :: Vector3d :: UnitZ ()))
return ;
double angle_z = ( rotation . axis () == Eigen :: Vector3d :: UnitZ ()) ? rotation . angle () : - rotation . angle ();
#endif
2018-01-30 09:27:10 +01:00
instance . offset . x = offset_x ;
instance . offset . y = offset_y ;
instance . scaling_factor = sx ;
instance . rotation = angle_z ;
}
2018-02-27 10:49:51 +01:00
bool _3MF_Importer :: _handle_start_config ( const char ** attributes , unsigned int num_attributes )
2018-01-30 09:27:10 +01:00
{
2018-02-27 10:49:51 +01:00
// do nothing
return true ;
2018-01-30 09:27:10 +01:00
}
2018-02-27 10:49:51 +01:00
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 15:46:54 +01:00
// apply volume's name and config data
2018-02-27 10:49:51 +01:00
for ( const Metadata & metadata : volume_data . metadata )
{
2018-02-28 12:11:41 +01:00
if ( metadata . key == NAME_KEY )
2018-02-27 15:46:54 +01:00
volume -> name = metadata . value ;
2018-02-28 12:11:41 +01:00
else if (( metadata . key == MODIFIER_KEY ) && ( metadata . value == "1" ))
volume -> modifier = true ;
2018-02-27 15:46:54 +01:00
else
volume -> config . set_deserialize ( metadata . key , metadata . value );
2018-02-27 10:49:51 +01:00
}
}
return true ;
}
void XMLCALL _3MF_Importer :: _handle_start_model_xml_element ( void * userData , const char * name , const char ** attributes )
2018-01-30 09:27:10 +01:00
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer != nullptr )
2018-02-27 10:49:51 +01:00
importer -> _handle_start_model_xml_element ( name , attributes );
}
void XMLCALL _3MF_Importer :: _handle_end_model_xml_element ( void * userData , const char * name )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer != nullptr )
importer -> _handle_end_model_xml_element ( name );
}
2018-04-30 15:27:01 +02: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 10:49:51 +01: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 )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer != nullptr )
importer -> _handle_end_config_xml_element ( name );
2018-01-30 09:27:10 +01:00
}
2018-02-14 14:30:03 +01:00
class _3MF_Exporter : public _3MF_Base
2018-02-08 13:26:50 +01:00
{
struct BuildItem
{
unsigned int id ;
Matrix4x4 matrix ;
2018-02-27 10:49:51 +01: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 13:26:50 +01:00
};
typedef std :: vector < BuildItem > BuildItemsList ;
2018-02-27 10:49:51 +01:00
typedef std :: map < int , ObjectData > IdToObjectDataMap ;
IdToObjectDataMap m_objects_data ;
2018-02-08 13:26:50 +01:00
public :
2018-03-22 13:49:48 +01:00
bool save_model_to_file ( const std :: string & filename , Model & model , const Print & print , bool export_print_config );
2018-02-08 13:26:50 +01:00
private :
2018-03-22 13:49:48 +01:00
bool _save_model_to_file ( const std :: string & filename , Model & model , const Print & print , bool export_print_config );
2018-02-13 15:19:55 +01: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 10:49:51 +01: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 13:26:50 +01:00
bool _add_build_to_model_stream ( std :: stringstream & stream , const BuildItemsList & build_items );
2018-04-27 12:56:35 +02:00
bool _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model );
2018-02-27 10:49:51 +01: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 13:26:50 +01:00
};
2018-03-22 13:49:48 +01:00
bool _3MF_Exporter :: save_model_to_file ( const std :: string & filename , Model & model , const Print & print , bool export_print_config )
2018-02-08 13:26:50 +01:00
{
2018-02-14 14:30:03 +01:00
clear_errors ();
2018-03-22 13:49:48 +01:00
return _save_model_to_file ( filename , model , print , export_print_config );
2018-02-08 13:26:50 +01:00
}
2018-03-22 13:49:48 +01:00
bool _3MF_Exporter :: _save_model_to_file ( const std :: string & filename , Model & model , const Print & print , bool export_print_config )
2018-02-08 13:26:50 +01:00
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive );
2018-02-27 10:49:51 +01:00
m_objects_data . clear ();
2018-02-08 13:26:50 +01:00
mz_bool res = mz_zip_writer_init_file ( & archive , filename . c_str (), 0 );
if ( res == 0 )
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to open the file" );
2018-02-08 13:26:50 +01:00
return false ;
}
// adds content types file
2018-02-13 15:19:55 +01:00
if ( ! _add_content_types_file_to_archive ( archive ))
2018-02-08 13:26:50 +01:00
{
mz_zip_writer_end ( & archive );
boost :: filesystem :: remove ( filename );
return false ;
}
// adds relationships file
2018-02-13 15:19:55 +01:00
if ( ! _add_relationships_file_to_archive ( archive ))
2018-02-08 13:26:50 +01:00
{
mz_zip_writer_end ( & archive );
boost :: filesystem :: remove ( filename );
return false ;
}
// adds model file
2018-02-13 15:19:55 +01:00
if ( ! _add_model_file_to_archive ( archive , model ))
{
mz_zip_writer_end ( & archive );
boost :: filesystem :: remove ( filename );
return false ;
}
2018-04-27 12:56:35 +02: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 10:49:51 +01:00
// adds slic3r print config file
2018-03-22 13:49:48 +01:00
if ( export_print_config )
2018-02-27 10:49:51 +01:00
{
2018-03-22 13:49:48 +01:00
if ( ! _add_print_config_file_to_archive ( archive , print ))
{
mz_zip_writer_end ( & archive );
boost :: filesystem :: remove ( filename );
return false ;
}
2018-02-27 10:49:51 +01:00
}
// adds slic3r model config file
if ( ! _add_model_config_file_to_archive ( archive , model ))
2018-02-08 13:26:50 +01: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 14:30:03 +01:00
add_error ( "Unable to finalize the archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
mz_zip_writer_end ( & archive );
return true ;
}
2018-02-13 15:19:55 +01:00
bool _3MF_Exporter :: _add_content_types_file_to_archive ( mz_zip_archive & archive )
2018-02-08 13:26:50 +01: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 14:30:03 +01:00
add_error ( "Unable to add content types file to archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
return true ;
}
2018-02-13 15:19:55 +01:00
bool _3MF_Exporter :: _add_relationships_file_to_archive ( mz_zip_archive & archive )
2018-02-08 13:26:50 +01: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 14:30:03 +01:00
add_error ( "Unable to add relationships file to archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
return true ;
}
2018-02-13 15:19:55 +01:00
bool _3MF_Exporter :: _add_model_file_to_archive ( mz_zip_archive & archive , Model & model )
2018-02-08 13:26:50 +01:00
{
std :: stringstream stream ;
stream << "<?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
2018-04-30 15:27:01 +02: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 10:49:51 +01:00
stream << " <" << RESOURCES_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
BuildItemsList build_items ;
unsigned int object_id = 1 ;
for ( ModelObject * obj : model . objects )
{
if ( obj == nullptr )
continue ;
2018-02-27 10:49:51 +01: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 13:26:50 +01:00
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to add object to archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
}
2018-02-27 10:49:51 +01:00
stream << " </" << RESOURCES_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
if ( ! _add_build_to_model_stream ( stream , build_items ))
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to add build to archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
2018-02-27 10:49:51 +01:00
stream << "</" << MODEL_TAG << "> \n " ;
2018-02-08 13:26:50 +01: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 14:30:03 +01:00
add_error ( "Unable to add model file to archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
return true ;
}
2018-02-27 10:49:51 +01: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 13:26:50 +01: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 10:49:51 +01:00
stream << " <" << OBJECT_TAG << " id= \" " << instance_id << " \" type= \" model \" > \n " ;
2018-02-08 13:26:50 +01:00
if ( id == 0 )
{
2018-02-27 10:49:51 +01:00
if ( ! _add_mesh_to_object_stream ( stream , object , volumes_offsets ))
2018-02-08 13:26:50 +01:00
{
2018-02-14 14:30:03 +01:00
add_error ( "Unable to add mesh to archive" );
2018-02-08 13:26:50 +01:00
return false ;
}
}
else
{
2018-02-27 10:49:51 +01:00
stream << " <" << COMPONENTS_TAG << "> \n " ;
stream << " <" << COMPONENT_TAG << " objectid= \" " << object_id << " \" /> \n " ;
stream << " </" << COMPONENTS_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
}
Eigen :: Affine3f transform ;
2018-04-12 10:10:15 +02: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 13:26:50 +01:00
build_items . emplace_back ( instance_id , transform . matrix ());
2018-02-27 10:49:51 +01:00
stream << " </" << OBJECT_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
++ id ;
}
object_id += id ;
return true ;
}
2018-02-27 10:49:51 +01:00
bool _3MF_Exporter :: _add_mesh_to_object_stream ( std :: stringstream & stream , ModelObject & object , VolumeToOffsetsMap & volumes_offsets )
2018-02-08 13:26:50 +01:00
{
2018-02-27 10:49:51 +01:00
stream << " <" << MESH_TAG << "> \n " ;
stream << " <" << VERTICES_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
unsigned int vertices_count = 0 ;
for ( ModelVolume * volume : object . volumes )
{
if ( volume == nullptr )
continue ;
2018-02-27 10:49:51 +01:00
VolumeToOffsetsMap :: iterator volume_it = volumes_offsets . insert ( VolumeToOffsetsMap :: value_type ( volume , Offsets ( vertices_count ))). first ;
2018-02-08 13:26:50 +01: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 14:30:03 +01:00
add_error ( "Found invalid mesh" );
2018-02-08 13:26:50 +01:00
return false ;
}
vertices_count += stl . stats . shared_vertices ;
for ( int i = 0 ; i < stl . stats . shared_vertices ; ++ i )
{
2018-02-27 10:49:51 +01:00
stream << " <" << VERTEX_TAG << " " ;
2018-04-12 10:10:15 +02: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 13:26:50 +01:00
}
}
2018-02-27 10:49:51 +01:00
stream << " </" << VERTICES_TAG << "> \n " ;
stream << " <" << TRIANGLES_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
2018-02-27 10:49:51 +01:00
unsigned int triangles_count = 0 ;
2018-02-08 13:26:50 +01:00
for ( ModelVolume * volume : object . volumes )
{
if ( volume == nullptr )
continue ;
2018-02-27 10:49:51 +01:00
VolumeToOffsetsMap :: iterator volume_it = volumes_offsets . find ( volume );
assert ( volume_it != volumes_offsets . end ());
2018-02-08 13:26:50 +01:00
stl_file & stl = volume -> mesh . stl ;
2018-02-27 10:49:51 +01: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 13:26:50 +01:00
for ( uint32_t i = 0 ; i < stl . stats . number_of_facets ; ++ i )
{
2018-02-27 10:49:51 +01:00
stream << " <" << TRIANGLE_TAG << " " ;
2018-02-08 13:26:50 +01:00
for ( int j = 0 ; j < 3 ; ++ j )
{
2018-02-27 10:49:51 +01:00
stream << "v" << j + 1 << "= \" " << stl . v_indices [ i ]. vertex [ j ] + volume_it -> second . first_vertex_id << " \" " ;
2018-02-08 13:26:50 +01:00
}
stream << "/> \n " ;
}
}
2018-02-27 10:49:51 +01:00
stream << " </" << TRIANGLES_TAG << "> \n " ;
stream << " </" << MESH_TAG << "> \n " ;
2018-02-08 13:26:50 +01: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 14:30:03 +01:00
add_error ( "No build item found" );
2018-02-08 13:26:50 +01:00
return false ;
}
2018-02-27 10:49:51 +01:00
stream << " <" << BUILD_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
for ( const BuildItem & item : build_items )
{
2018-02-27 10:49:51 +01:00
stream << " <" << ITEM_TAG << " objectid= \" " << item . id << " \" transform = \" " ;
2018-02-08 13:26:50 +01: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 10:49:51 +01:00
stream << " </" << BUILD_TAG << "> \n " ;
2018-02-08 13:26:50 +01:00
return true ;
}
2018-04-27 12:56:35 +02: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 10:49:51 +01:00
bool _3MF_Exporter :: _add_print_config_file_to_archive ( mz_zip_archive & archive , const Print & print )
2018-01-30 09:27:10 +01:00
{
2018-02-13 15:19:55 +01: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 12:56:35 +02:00
if ( ! out . empty ())
2018-02-13 15:19:55 +01:00
{
2018-04-27 12:56:35 +02: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 10:49:51 +01: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 15:46:54 +01: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 10:49:51 +01: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 15:46:54 +01:00
// stores volume's name
if ( ! volume -> name . empty ())
2018-02-28 12:11:41 +01: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 15:46:54 +01:00
2018-02-27 10:49:51 +01: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 15:46:54 +01:00
2018-02-27 10:49:51 +01: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 15:19:55 +01:00
return false ;
}
return true ;
}
bool load_3mf ( const char * path , PresetBundle * bundle , Model * model )
{
if (( path == nullptr ) || ( bundle == nullptr ) || ( model == nullptr ))
2018-01-30 09:27:10 +01:00
return false ;
_3MF_Importer importer ;
2018-02-13 15:19:55 +01:00
bool res = importer . load_model_from_file ( path , * model , * bundle );
2018-04-27 12:56:35 +02:00
importer . log_errors ();
2018-01-30 09:27:10 +01:00
return res ;
}
2018-02-08 13:26:50 +01:00
2018-03-22 13:49:48 +01:00
bool store_3mf ( const char * path , Model * model , Print * print , bool export_print_config )
2018-02-08 13:26:50 +01:00
{
2018-02-13 15:19:55 +01:00
if (( path == nullptr ) || ( model == nullptr ) || ( print == nullptr ))
2018-02-08 13:26:50 +01:00
return false ;
_3MF_Exporter exporter ;
2018-03-22 13:49:48 +01:00
bool res = exporter . save_model_to_file ( path , * model , * print , export_print_config );
2018-02-08 13:26:50 +01:00
if ( ! res )
2018-02-14 14:30:03 +01:00
exporter . log_errors ();
2018-02-08 13:26:50 +01:00
return res ;
}
2018-01-30 09:27:10 +01:00
} // namespace Slic3r