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"
2018-10-12 12:19:57 +02:00
#include "../Geometry.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-10-05 10:13:21 +02:00
const std :: string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.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-09-17 15:12:13 +02:00
const char * VOLUME_TYPE_KEY = "volume_type" ;
2018-02-28 12:11:41 +01:00
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"
};
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 ;
}
2018-08-28 14:30:36 +02:00
Slic3r :: Transform3d get_transform_from_string ( const std :: string & mat_str )
2018-01-30 09:27:10 +01:00
{
if ( mat_str . empty ())
// empty string means default identity matrix
2018-08-28 14:30:36 +02:00
return Slic3r :: Transform3d :: Identity ();
2018-01-30 09:27:10 +01:00
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
2018-08-28 14:30:36 +02:00
return Slic3r :: Transform3d :: Identity ();
2018-01-30 09:27:10 +01:00
2018-08-28 14:30:36 +02:00
Slic3r :: Transform3d ret = Slic3r :: Transform3d :: Identity ();
2018-01-30 09:27:10 +01:00
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 )
{
2018-08-28 14:30:36 +02:00
ret ( r , c ) = :: atof ( mat_elements_str [ i ++ ]. c_str ());
2018-01-30 09:27:10 +01:00
}
}
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 ;
2018-08-28 14:30:36 +02:00
Transform3d transform ;
2018-01-30 09:27:10 +01:00
2018-02-27 10:49:51 +01:00
explicit Component ( int object_id )
: object_id ( object_id )
2018-08-28 14:30:36 +02:00
, transform ( Transform3d :: Identity ())
2018-02-27 10:49:51 +01:00
{
}
2018-08-28 14:30:36 +02:00
Component ( int object_id , const Transform3d & transform )
2018-02-27 10:49:51 +01:00
: object_id ( object_id )
2018-08-28 14:30:36 +02:00
, transform ( transform )
2018-02-27 10:49:51 +01:00
{
}
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 ;
2018-08-28 14:30:36 +02:00
Transform3d transform ;
2018-01-30 09:27:10 +01:00
2018-08-28 14:30:36 +02:00
Instance ( ModelInstance * instance , const Transform3d & transform )
2018-02-27 10:49:51 +01:00
: instance ( instance )
2018-08-28 14:30:36 +02:00
, transform ( transform )
2018-02-27 10:49:51 +01:00
{
}
};
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-10-05 10:13:21 +02:00
typedef std :: map < int , std :: vector < Vec3f >> IdToSlaSupportPointsMap ;
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-10-05 10:13:21 +02:00
IdToSlaSupportPointsMap m_sla_support_points ;
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-09-25 11:53:05 +02:00
bool load_model_from_file ( const std :: string & filename , Model & model , DynamicPrintConfig & config );
2018-01-30 09:27:10 +01:00
private :
void _destroy_xml_parser ();
void _stop_xml_parser ();
2018-09-25 11:53:05 +02:00
bool _load_model_from_file ( const std :: string & filename , Model & model , DynamicPrintConfig & config );
2018-02-13 15:19:55 +01:00
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 );
2018-10-05 10:13:21 +02:00
void _extract_sla_support_points_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat );
2018-10-26 15:45:52 +02:00
2018-09-25 11:53:05 +02:00
void _extract_print_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig & config , 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-08-28 14:30:36 +02:00
bool _create_object_instance ( int object_id , const Transform3d & transform , unsigned int recur_counter );
2018-01-30 09:27:10 +01:00
2018-08-28 14:30:36 +02:00
void _apply_transform ( ModelInstance & instance , const Transform3d & transform );
2018-01-30 09:27:10 +01:00
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-09-25 11:53:05 +02:00
bool _3MF_Importer :: load_model_from_file ( const std :: string & filename , Model & model , DynamicPrintConfig & config )
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-10-05 10:13:21 +02:00
m_sla_support_points . 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-09-25 11:53:05 +02:00
return _load_model_from_file ( filename , model , config );
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-09-25 11:53:05 +02:00
bool _3MF_Importer :: _load_model_from_file ( const std :: string & filename , Model & model , DynamicPrintConfig & config )
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-10-05 10:13:21 +02:00
else if ( boost :: algorithm :: iequals ( name , SLA_SUPPORT_POINTS_FILE ))
{
// extract sla support points file
_extract_sla_support_points_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-09-25 11:53:05 +02:00
_extract_print_config_from_archive ( archive , stat , config , 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-10-05 10:13:21 +02:00
IdToSlaSupportPointsMap :: iterator obj_sla_support_points = m_sla_support_points . find ( object . first );
if ( obj_sla_support_points != m_sla_support_points . end () && ! obj_sla_support_points -> second . empty ())
object . second -> sla_support_points = obj_sla_support_points -> second ;
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-09-25 11:53:05 +02:00
void _3MF_Importer :: _extract_print_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig & config , 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
}
2018-09-25 11:53:05 +02:00
config . load_from_gcode_string ( buffer . data ());
2018-02-13 15:19:55 +01:00
}
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-10-05 10:13:21 +02:00
void _3MF_Importer :: _extract_sla_support_points_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 sla support points 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 ;
}
IdToSlaSupportPointsMap :: iterator object_item = m_sla_support_points . find ( object_id );
if ( object_item != m_sla_support_points . end ())
{
add_error ( "Found duplicated SLA support points" );
continue ;
}
std :: vector < std :: string > object_data_points ;
boost :: split ( object_data_points , object_data [ 1 ], boost :: is_any_of ( " " ), boost :: token_compress_off );
std :: vector < Vec3f > sla_support_points ;
for ( unsigned int i = 0 ; i < object_data_points . size (); i += 3 )
sla_support_points . push_back ( Vec3f ( std :: atof ( object_data_points [ i + 0 ]. c_str ()), std :: atof ( object_data_points [ i + 1 ]. c_str ()), std :: atof ( object_data_points [ i + 2 ]. c_str ())));
if ( ! sla_support_points . empty ())
m_sla_support_points . insert ( IdToSlaSupportPointsMap :: value_type ( object_id , sla_support_points ));
}
}
}
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-08-28 14:30:36 +02:00
// apply the transform to the instance
_apply_transform ( * instance . instance , instance . transform );
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 );
2018-08-28 14:30:36 +02:00
Transform3d transform = get_transform_from_string ( get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ));
2018-01-30 09:27:10 +01:00
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 ;
}
}
2018-08-28 14:30:36 +02:00
m_curr_object . components . emplace_back ( object_id , transform );
2018-01-30 09:27:10 +01:00
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 );
2018-08-28 14:30:36 +02:00
Transform3d transform = get_transform_from_string ( get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ));
2018-01-30 09:27:10 +01:00
2018-08-28 14:30:36 +02:00
return _create_object_instance ( object_id , transform , 1 );
2018-01-30 09:27:10 +01:00
}
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-08-28 14:30:36 +02:00
bool _3MF_Importer :: _create_object_instance ( int object_id , const Transform3d & transform , unsigned int recur_counter )
2018-01-30 09:27:10 +01:00
{
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 ;
}
2018-08-28 14:30:36 +02:00
m_instances . emplace_back ( instance , transform );
2018-01-30 09:27:10 +01:00
}
}
else
{
// recursively process nested components
for ( const Component & component : it -> second )
{
2018-08-28 14:30:36 +02:00
if ( ! _create_object_instance ( component . object_id , transform * component . transform , recur_counter + 1 ))
2018-01-30 09:27:10 +01:00
return false ;
}
}
return true ;
}
2018-08-28 14:30:36 +02:00
void _3MF_Importer :: _apply_transform ( ModelInstance & instance , const Transform3d & transform )
2018-01-30 09:27:10 +01:00
{
// translation
2018-10-12 12:19:57 +02:00
Vec3d offset = transform . matrix (). block ( 0 , 3 , 3 , 1 );
2018-01-30 09:27:10 +01:00
2018-10-12 12:19:57 +02:00
Eigen :: Matrix < double , 3 , 3 , Eigen :: DontAlign > m3x3 = transform . matrix (). block ( 0 , 0 , 3 , 3 );
2018-10-22 13:27:53 +02:00
// mirror
// it is impossible to reconstruct the original mirroring factors from a matrix,
// we can only detect if the matrix contains a left handed reference system
// in which case we reorient it back to right handed by mirroring the x axis
Vec3d mirror = Vec3d :: Ones ();
if ( m3x3 . col ( 0 ). dot ( m3x3 . col ( 1 ). cross ( m3x3 . col ( 2 ))) < 0.0 )
{
mirror ( 0 ) = - 1.0 ;
// remove mirror
m3x3 . col ( 0 ) *= - 1.0 ;
}
2018-01-30 09:27:10 +01:00
// scale
2018-10-12 12:19:57 +02:00
Vec3d scale ( m3x3 . col ( 0 ). norm (), m3x3 . col ( 1 ). norm (), m3x3 . col ( 2 ). norm ());
2018-01-30 09:27:10 +01:00
// invalid scale value, return
2018-10-12 12:19:57 +02:00
if (( scale ( 0 ) == 0.0 ) || ( scale ( 1 ) == 0.0 ) || ( scale ( 2 ) == 0.0 ))
2018-01-30 09:27:10 +01:00
return ;
2018-10-12 12:19:57 +02:00
// remove scale
m3x3 . col ( 0 ). normalize ();
m3x3 . col ( 1 ). normalize ();
m3x3 . col ( 2 ). normalize ();
2018-01-30 09:27:10 +01:00
2018-10-12 12:19:57 +02:00
Vec3d rotation = Slic3r :: Geometry :: extract_euler_angles ( m3x3 );
2018-09-25 10:42:11 +02:00
instance . set_offset ( offset );
2018-10-12 12:19:57 +02:00
instance . set_scaling_factor ( scale );
2018-09-25 10:42:11 +02:00
instance . set_rotation ( rotation );
2018-10-22 13:27:53 +02:00
instance . set_mirror ( mirror );
2018-01-30 09:27:10 +01:00
}
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 ;
}
2018-10-08 15:17:36 +02:00
m_curr_config . volume_id = ( int ) object -> second . volumes . size ();
2018-02-27 10:49:51 +01:00
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 ;
}
2018-10-08 15:17:36 +02:00
unsigned int geo_tri_count = ( unsigned int ) geometry . triangles . size () / 3 ;
2018-02-27 10:49:51 +01:00
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 ;
2018-10-08 15:17:36 +02:00
for ( unsigned int i = 0 ; i < triangles_count ; ++ i )
2018-02-27 10:49:51 +01:00
{
unsigned int ii = i * 3 ;
stl_facet & facet = stl . facet_start [ i ];
for ( unsigned int v = 0 ; v < 3 ; ++ v )
{
2018-08-22 15:03:35 +02:00
:: memcpy ( facet . vertex [ v ]. data (), ( const void * ) & geometry . vertices [ geometry . triangles [ src_start_id + ii + v ] * 3 ], 3 * sizeof ( float ));
2018-02-27 10:49:51 +01:00
}
}
stl_get_size ( & stl );
volume -> mesh . repair ();
2018-08-15 12:50:06 +02:00
volume -> calculate_convex_hull ();
2018-02-27 10:49:51 +01:00
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" ))
2018-09-17 15:12:13 +02:00
volume -> set_type ( ModelVolume :: PARAMETER_MODIFIER );
else if ( metadata . key == VOLUME_TYPE_KEY )
volume -> set_type ( ModelVolume :: type_from_string ( metadata . value ));
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 ;
2018-08-28 14:30:36 +02:00
Transform3d transform ;
2018-02-08 13:26:50 +01:00
2018-08-28 14:30:36 +02:00
BuildItem ( unsigned int id , const Transform3d & transform )
2018-02-27 10:49:51 +01:00
: id ( id )
2018-08-28 14:30:36 +02:00
, transform ( transform )
2018-02-27 10:49:51 +01:00
{
}
};
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-10-05 10:13:21 +02:00
bool _add_sla_support_points_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-10-05 10:13:21 +02:00
// adds sla support points file
if ( ! _add_sla_support_points_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
}
2018-09-05 14:02:08 +02:00
Transform3d t = instance -> world_matrix ();
2018-08-28 14:30:36 +02:00
build_items . emplace_back ( instance_id , t );
2018-02-08 13:26:50 +01:00
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-08-22 15:03:35 +02:00
stream << "x= \" " << stl . v_shared [ i ]( 0 ) << " \" " ;
stream << "y= \" " << stl . v_shared [ i ]( 1 ) << " \" " ;
stream << "z= \" " << stl . v_shared [ i ]( 2 ) << " \" /> \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 )
{
2018-08-28 14:30:36 +02:00
stream << item . transform ( r , c );
2018-02-08 13:26:50 +01:00
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-10-05 10:13:21 +02:00
bool _3MF_Exporter :: _add_sla_support_points_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 ;
const std :: vector < Vec3f >& sla_support_points = object -> sla_support_points ;
if ( ! sla_support_points . empty ())
{
sprintf ( buffer , "object_id=%d|" , count );
out += buffer ;
// Store the layer height profile as a single space separated list.
for ( size_t i = 0 ; i < sla_support_points . size (); ++ i )
{
sprintf ( buffer , ( i == 0 ? "%f %f %f" : " %f %f %f" ), sla_support_points [ i ]( 0 ), sla_support_points [ i ]( 1 ), sla_support_points [ i ]( 2 ));
out += buffer ;
}
out += " \n " ;
}
}
if ( ! out . empty ())
{
if ( ! mz_zip_writer_add_mem ( & archive , SLA_SUPPORT_POINTS_FILE . c_str (), ( const void * ) out . data (), out . length (), MZ_DEFAULT_COMPRESSION ))
{
add_error ( "Unable to add sla support points 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 ())
2018-07-23 14:39:50 +02:00
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "= \" " << OBJECT_TYPE << " \" " << KEY_ATTR << "= \" name \" " << VALUE_ATTR << "= \" " << xml_escape ( obj -> name ) << " \" /> \n " ;
2018-02-27 15:46:54 +01:00
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-07-23 14:39:50 +02:00
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "= \" " << VOLUME_TYPE << " \" " << KEY_ATTR << "= \" " << NAME_KEY << " \" " << VALUE_ATTR << "= \" " << xml_escape ( volume -> name ) << " \" /> \n " ;
2018-02-28 12:11:41 +01:00
2018-09-17 15:12:13 +02:00
// stores volume's modifier field (legacy, to support old slicers)
if ( volume -> is_modifier ())
2018-02-28 12:11:41 +01:00
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "= \" " << VOLUME_TYPE << " \" " << KEY_ATTR << "= \" " << MODIFIER_KEY << " \" " << VALUE_ATTR << "= \" 1 \" /> \n " ;
2018-09-17 15:12:13 +02:00
// stores volume's type (overrides the modifier field above)
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "= \" " << VOLUME_TYPE << " \" " << KEY_ATTR << "= \" " << VOLUME_TYPE_KEY << " \" " <<
VALUE_ATTR << "= \" " << ModelVolume :: type_to_string ( volume -> type ()) << " \" /> \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 ;
}
2018-09-25 11:53:05 +02:00
bool load_3mf ( const char * path , DynamicPrintConfig * config , Model * model )
2018-02-13 15:19:55 +01:00
{
2018-09-25 11:53:05 +02:00
if (( path == nullptr ) || ( config == nullptr ) || ( model == nullptr ))
2018-01-30 09:27:10 +01:00
return false ;
_3MF_Importer importer ;
2018-09-25 11:53:05 +02:00
bool res = importer . load_model_from_file ( path , * model , * config );
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