2018-01-30 08:27:10 +00:00
# include "../libslic3r.h"
# include "../Model.hpp"
2018-02-13 14:19:55 +00:00
# include "../Utils.hpp"
# include "../GCode.hpp"
2018-10-12 10:19:57 +00:00
# include "../Geometry.hpp"
2019-10-23 11:31:24 +00:00
# include "../GCode/ThumbnailData.hpp"
2020-07-21 07:08:48 +00:00
# include "../Time.hpp"
2018-01-30 08:27:10 +00:00
2019-08-26 07:35:04 +00:00
# include "../I18N.hpp"
2018-01-30 08:27:10 +00:00
# include "3mf.hpp"
2019-02-27 09:31:53 +00:00
# include <limits>
2019-09-02 09:55:14 +00:00
# include <stdexcept>
2019-02-27 09:31:53 +00:00
2018-01-30 08:27:10 +00:00
# include <boost/algorithm/string/classification.hpp>
# include <boost/algorithm/string/split.hpp>
2018-02-13 14:19:55 +00:00
# include <boost/algorithm/string/predicate.hpp>
2019-06-11 12:54:31 +00:00
# include <boost/algorithm/string/replace.hpp>
2018-02-08 12:26:50 +00:00
# include <boost/filesystem/operations.hpp>
# include <boost/nowide/fstream.hpp>
2019-06-02 20:27:35 +00:00
# include <boost/nowide/cstdio.hpp>
2018-01-30 08:27:10 +00:00
2019-06-27 11:54:51 +00:00
# include <boost/property_tree/ptree.hpp>
# include <boost/property_tree/xml_parser.hpp>
# include <boost/foreach.hpp>
namespace pt = boost : : property_tree ;
2018-01-30 08:27:10 +00:00
# include <expat.h>
2018-02-13 14:33:45 +00:00
# include <Eigen/Dense>
2019-06-03 13:27:46 +00:00
# include "miniz_extension.hpp"
2018-01-30 08:27:10 +00:00
2018-04-30 13:27:01 +00:00
// VERSION NUMBERS
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
2019-12-19 09:22:46 +00:00
// 2 : Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file, meshes transformed back to their coordinate system on loading.
2019-12-19 08:41:30 +00:00
// WARNING !! -> the version number has been rolled back to 1
// the next change should use 3
const unsigned int VERSION_3MF = 1 ;
2019-12-19 09:22:46 +00:00
// Allow loading version 2 file as well.
const unsigned int VERSION_3MF_COMPATIBLE = 2 ;
2018-04-30 13:27:01 +00:00
const char * SLIC3RPE_3MF_VERSION = " slic3rpe:Version3mf " ; // definition of the metadata name saved into .model file
2018-02-08 12:26:50 +00:00
const std : : string MODEL_FOLDER = " 3D/ " ;
2018-01-30 08:27:10 +00:00
const std : : string MODEL_EXTENSION = " .model " ;
2018-02-08 12:26:50 +00:00
const std : : string MODEL_FILE = " 3D/3dmodel.model " ; // << this is the only format of the string which works with CURA
const std : : string CONTENT_TYPES_FILE = " [Content_Types].xml " ;
const std : : string RELATIONSHIPS_FILE = " _rels/.rels " ;
2019-10-23 11:31:24 +00:00
const std : : string THUMBNAIL_FILE = " Metadata/thumbnail.png " ;
2018-02-27 09:49:51 +00:00
const std : : string PRINT_CONFIG_FILE = " Metadata/Slic3r_PE.config " ;
const std : : string MODEL_CONFIG_FILE = " Metadata/Slic3r_PE_model.config " ;
2018-04-27 10:56:35 +00:00
const std : : string LAYER_HEIGHTS_PROFILE_FILE = " Metadata/Slic3r_PE_layer_heights_profile.txt " ;
2019-06-27 11:54:51 +00:00
const std : : string LAYER_CONFIG_RANGES_FILE = " Metadata/Prusa_Slicer_layer_config_ranges.xml " ;
2018-10-05 08:13:21 +00:00
const std : : string SLA_SUPPORT_POINTS_FILE = " Metadata/Slic3r_PE_sla_support_points.txt " ;
2019-12-06 14:47:58 +00:00
const std : : string SLA_DRAIN_HOLES_FILE = " Metadata/Slic3r_PE_sla_drain_holes.txt " ;
2019-12-17 07:37:50 +00:00
const std : : string CUSTOM_GCODE_PER_PRINT_Z_FILE = " Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml " ;
2018-01-30 08:27:10 +00:00
const char * MODEL_TAG = " model " ;
const char * RESOURCES_TAG = " resources " ;
const char * OBJECT_TAG = " object " ;
const char * MESH_TAG = " mesh " ;
const char * VERTICES_TAG = " vertices " ;
const char * VERTEX_TAG = " vertex " ;
const char * TRIANGLES_TAG = " triangles " ;
const char * TRIANGLE_TAG = " triangle " ;
const char * COMPONENTS_TAG = " components " ;
const char * COMPONENT_TAG = " component " ;
const char * BUILD_TAG = " build " ;
const char * ITEM_TAG = " item " ;
2018-04-30 13:27:01 +00:00
const char * METADATA_TAG = " metadata " ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
const char * CONFIG_TAG = " config " ;
const char * VOLUME_TAG = " volume " ;
2018-01-30 08:27:10 +00:00
const char * UNIT_ATTR = " unit " ;
const char * NAME_ATTR = " name " ;
const char * TYPE_ATTR = " type " ;
const char * ID_ATTR = " id " ;
const char * X_ATTR = " x " ;
const char * Y_ATTR = " y " ;
const char * Z_ATTR = " z " ;
const char * V1_ATTR = " v1 " ;
const char * V2_ATTR = " v2 " ;
const char * V3_ATTR = " v3 " ;
const char * OBJECTID_ATTR = " objectid " ;
const char * TRANSFORM_ATTR = " transform " ;
2019-08-06 12:54:38 +00:00
const char * PRINTABLE_ATTR = " printable " ;
2020-01-02 10:37:50 +00:00
const char * INSTANCESCOUNT_ATTR = " instances_count " ;
2020-07-24 14:53:05 +00:00
const char * CUSTOM_SUPPORTS_ATTR = " slic3rpe:custom_supports " ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
const char * KEY_ATTR = " key " ;
const char * VALUE_ATTR = " value " ;
const char * FIRST_TRIANGLE_ID_ATTR = " firstid " ;
const char * LAST_TRIANGLE_ID_ATTR = " lastid " ;
const char * OBJECT_TYPE = " object " ;
const char * VOLUME_TYPE = " volume " ;
2018-02-28 11:11:41 +00:00
const char * NAME_KEY = " name " ;
const char * MODIFIER_KEY = " modifier " ;
2018-09-17 13:12:13 +00:00
const char * VOLUME_TYPE_KEY = " volume_type " ;
2019-09-19 07:09:11 +00:00
const char * MATRIX_KEY = " matrix " ;
const char * SOURCE_FILE_KEY = " source_file " ;
const char * SOURCE_OBJECT_ID_KEY = " source_object_id " ;
const char * SOURCE_VOLUME_ID_KEY = " source_volume_id " ;
const char * SOURCE_OFFSET_X_KEY = " source_offset_x " ;
const char * SOURCE_OFFSET_Y_KEY = " source_offset_y " ;
const char * SOURCE_OFFSET_Z_KEY = " source_offset_z " ;
2018-02-28 11:11:41 +00:00
2018-01-30 08:27:10 +00:00
const unsigned int VALID_OBJECT_TYPES_COUNT = 1 ;
const char * VALID_OBJECT_TYPES [ ] =
{
" model "
} ;
const unsigned int INVALID_OBJECT_TYPES_COUNT = 4 ;
const char * INVALID_OBJECT_TYPES [ ] =
{
" solidsupport " ,
" support " ,
" surface " ,
" other "
} ;
2019-09-02 09:55:14 +00:00
class version_error : public std : : runtime_error
{
public :
version_error ( const std : : string & what_arg ) : std : : runtime_error ( what_arg ) { }
version_error ( const char * what_arg ) : std : : runtime_error ( what_arg ) { }
} ;
2018-01-30 08:27:10 +00:00
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 ;
}
2019-08-06 12:54:38 +00:00
bool get_attribute_value_bool ( 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 ) ? ( bool ) : : atoi ( text ) : true ;
}
2019-09-19 07:09:11 +00:00
Slic3r : : Transform3d get_transform_from_3mf_specs_string ( const std : : string & mat_str )
2018-01-30 08:27:10 +00:00
{
2019-09-19 07:09:11 +00:00
// check: https://3mf.io/3d-manufacturing-format/ or https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md
// to see how matrices are stored inside 3mf according to specifications
Slic3r : : Transform3d ret = Slic3r : : Transform3d : : Identity ( ) ;
2018-01-30 08:27:10 +00:00
if ( mat_str . empty ( ) )
// empty string means default identity matrix
2019-09-19 07:09:11 +00:00
return ret ;
2018-01-30 08:27:10 +00: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
2019-09-19 07:09:11 +00:00
return ret ;
2018-01-30 08:27:10 +00: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 12:30:36 +00:00
ret ( r , c ) = : : atof ( mat_elements_str [ i + + ] . c_str ( ) ) ;
2018-01-30 08:27:10 +00: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 {
2019-08-26 07:35:04 +00:00
//! macro used to mark string used at localization,
//! return same string
# define L(s) (s)
# define _(s) Slic3r::I18N::translate(s)
2018-02-14 13:30:03 +00:00
// Base class with error messages management
class _3MF_Base
{
std : : vector < std : : string > m_errors ;
protected :
void add_error ( const std : : string & error ) { m_errors . push_back ( error ) ; }
void clear_errors ( ) { m_errors . clear ( ) ; }
public :
void log_errors ( )
{
for ( const std : : string & error : m_errors )
{
printf ( " %s \n " , error . c_str ( ) ) ;
}
}
} ;
class _3MF_Importer : public _3MF_Base
2018-01-30 08:27:10 +00:00
{
struct Component
{
int object_id ;
2018-08-28 12:30:36 +00:00
Transform3d transform ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
explicit Component ( int object_id )
: object_id ( object_id )
2018-08-28 12:30:36 +00:00
, transform ( Transform3d : : Identity ( ) )
2018-02-27 09:49:51 +00:00
{
}
2018-08-28 12:30:36 +00:00
Component ( int object_id , const Transform3d & transform )
2018-02-27 09:49:51 +00:00
: object_id ( object_id )
2018-08-28 12:30:36 +00:00
, transform ( transform )
2018-02-27 09:49:51 +00:00
{
}
2018-01-30 08:27:10 +00:00
} ;
typedef std : : vector < Component > ComponentsList ;
2018-02-27 09:49:51 +00:00
struct Geometry
2018-01-30 08:27:10 +00:00
{
2018-02-27 09:49:51 +00:00
std : : vector < float > vertices ;
std : : vector < unsigned int > triangles ;
2020-07-24 14:53:05 +00:00
std : : vector < std : : string > custom_supports ;
2018-02-27 09:49:51 +00:00
bool empty ( )
2018-01-30 08:27:10 +00:00
{
2018-02-27 09:49:51 +00:00
return vertices . empty ( ) | | triangles . empty ( ) ;
}
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
void reset ( )
{
vertices . clear ( ) ;
triangles . clear ( ) ;
2020-07-24 14:53:05 +00:00
custom_supports . clear ( ) ;
2018-02-27 09:49:51 +00:00
}
} ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
struct CurrentObject
{
2019-05-24 10:17:54 +00:00
// ID of the object inside the 3MF file, 1 based.
2018-01-30 08:27:10 +00:00
int id ;
2019-05-24 10:17:54 +00:00
// Index of the ModelObject in its respective Model, zero based.
int model_object_idx ;
2018-01-30 08:27:10 +00:00
Geometry geometry ;
ModelObject * object ;
ComponentsList components ;
2018-02-27 09:49:51 +00:00
CurrentObject ( )
{
reset ( ) ;
}
void reset ( )
{
id = - 1 ;
2019-05-24 10:17:54 +00:00
model_object_idx = - 1 ;
2018-02-27 09:49:51 +00:00
geometry . reset ( ) ;
object = nullptr ;
components . clear ( ) ;
}
} ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
struct CurrentConfig
{
int object_id ;
int volume_id ;
2018-01-30 08:27:10 +00:00
} ;
struct Instance
{
ModelInstance * instance ;
2018-08-28 12:30:36 +00:00
Transform3d transform ;
2018-01-30 08:27:10 +00:00
2018-08-28 12:30:36 +00:00
Instance ( ModelInstance * instance , const Transform3d & transform )
2018-02-27 09:49:51 +00:00
: instance ( instance )
2018-08-28 12:30:36 +00:00
, transform ( transform )
2018-02-27 09:49:51 +00: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 08:27:10 +00:00
} ;
2019-05-24 10:17:54 +00:00
// Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
typedef std : : map < int , int > IdToModelObjectMap ;
2018-01-30 08:27:10 +00:00
typedef std : : map < int , ComponentsList > IdToAliasesMap ;
typedef std : : vector < Instance > InstancesList ;
2018-02-27 09:49:51 +00:00
typedef std : : map < int , ObjectMetadata > IdToMetadataMap ;
typedef std : : map < int , Geometry > IdToGeometryMap ;
2018-04-27 10:56:35 +00:00
typedef std : : map < int , std : : vector < coordf_t > > IdToLayerHeightsProfileMap ;
2019-06-11 12:39:41 +00:00
typedef std : : map < int , t_layer_config_ranges > IdToLayerConfigRangesMap ;
2019-01-30 07:26:23 +00:00
typedef std : : map < int , std : : vector < sla : : SupportPoint > > IdToSlaSupportPointsMap ;
2019-12-06 14:47:58 +00:00
typedef std : : map < int , std : : vector < sla : : DrainHole > > IdToSlaDrainHolesMap ;
2018-01-30 08:27:10 +00:00
2018-04-30 13:27:01 +00:00
// Version of the 3mf file
unsigned int m_version ;
2019-08-23 11:12:31 +00:00
bool m_check_version ;
2018-04-30 13:27:01 +00:00
2018-01-30 08:27:10 +00:00
XML_Parser m_xml_parser ;
Model * m_model ;
float m_unit_factor ;
CurrentObject m_curr_object ;
IdToModelObjectMap m_objects ;
IdToAliasesMap m_objects_aliases ;
InstancesList m_instances ;
2018-02-27 09:49:51 +00:00
IdToGeometryMap m_geometries ;
CurrentConfig m_curr_config ;
IdToMetadataMap m_objects_metadata ;
2018-04-27 10:56:35 +00:00
IdToLayerHeightsProfileMap m_layer_heights_profiles ;
2019-06-11 12:39:41 +00:00
IdToLayerConfigRangesMap m_layer_config_ranges ;
2018-10-05 08:13:21 +00:00
IdToSlaSupportPointsMap m_sla_support_points ;
2019-12-06 14:47:58 +00:00
IdToSlaDrainHolesMap m_sla_drain_holes ;
2018-04-30 13:27:01 +00:00
std : : string m_curr_metadata_name ;
std : : string m_curr_characters ;
2018-11-08 09:18:19 +00:00
std : : string m_name ;
2018-01-30 08:27:10 +00:00
public :
_3MF_Importer ( ) ;
~ _3MF_Importer ( ) ;
2019-08-23 11:12:31 +00:00
bool load_model_from_file ( const std : : string & filename , Model & model , DynamicPrintConfig & config , bool check_version ) ;
2018-01-30 08:27:10 +00:00
private :
void _destroy_xml_parser ( ) ;
void _stop_xml_parser ( ) ;
2018-09-25 09:53:05 +00:00
bool _load_model_from_file ( const std : : string & filename , Model & model , DynamicPrintConfig & config ) ;
2018-02-13 14:19:55 +00:00
bool _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2018-04-27 10:56:35 +00:00
void _extract_layer_heights_profile_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2019-06-11 12:39:41 +00:00
void _extract_layer_config_ranges_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2018-10-05 08:13:21 +00:00
void _extract_sla_support_points_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2019-12-06 14:47:58 +00:00
void _extract_sla_drain_holes_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2018-10-26 13:45:52 +00:00
2019-12-17 07:37:50 +00:00
void _extract_custom_gcode_per_print_z_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
2019-11-11 15:01:34 +00:00
2018-09-25 09:53:05 +00: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 09:49:51 +00:00
bool _extract_model_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model ) ;
// handlers to parse the .model file
void _handle_start_model_xml_element ( const char * name , const char * * attributes ) ;
void _handle_end_model_xml_element ( const char * name ) ;
2018-04-30 13:27:01 +00:00
void _handle_model_xml_characters ( const XML_Char * s , int len ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
// handlers to parse the MODEL_CONFIG_FILE file
void _handle_start_config_xml_element ( const char * name , const char * * attributes ) ;
void _handle_end_config_xml_element ( const char * name ) ;
2018-01-30 08:27:10 +00:00
bool _handle_start_model ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_model ( ) ;
bool _handle_start_resources ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_resources ( ) ;
bool _handle_start_object ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_object ( ) ;
bool _handle_start_mesh ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_mesh ( ) ;
bool _handle_start_vertices ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_vertices ( ) ;
bool _handle_start_vertex ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_vertex ( ) ;
bool _handle_start_triangles ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_triangles ( ) ;
bool _handle_start_triangle ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_triangle ( ) ;
bool _handle_start_components ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_components ( ) ;
bool _handle_start_component ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_component ( ) ;
bool _handle_start_build ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_build ( ) ;
bool _handle_start_item ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_item ( ) ;
2018-04-30 13:27:01 +00:00
bool _handle_start_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_metadata ( ) ;
2019-08-06 12:54:38 +00:00
bool _create_object_instance ( int object_id , const Transform3d & transform , const bool printable , unsigned int recur_counter ) ;
2018-01-30 08:27:10 +00:00
2018-08-28 12:30:36 +00:00
void _apply_transform ( ModelInstance & instance , const Transform3d & transform ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config_object ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_object ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config_volume ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_volume ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _handle_start_config_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_metadata ( ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
bool _generate_volumes ( ModelObject & object , const Geometry & geometry , const ObjectMetadata : : VolumeMetadataList & volumes ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
// callbacks to parse the .model file
static void XMLCALL _handle_start_model_xml_element ( void * userData , const char * name , const char * * attributes ) ;
static void XMLCALL _handle_end_model_xml_element ( void * userData , const char * name ) ;
2018-04-30 13:27:01 +00:00
static void XMLCALL _handle_model_xml_characters ( void * userData , const XML_Char * s , int len ) ;
2018-01-30 08:27:10 +00:00
2018-02-27 09:49:51 +00:00
// callbacks to parse the MODEL_CONFIG_FILE file
static void XMLCALL _handle_start_config_xml_element ( void * userData , const char * name , const char * * attributes ) ;
static void XMLCALL _handle_end_config_xml_element ( void * userData , const char * name ) ;
} ;
2018-01-30 08:27:10 +00:00
_3MF_Importer : : _3MF_Importer ( )
2018-04-30 13:27:01 +00:00
: m_version ( 0 )
2019-08-23 11:12:31 +00:00
, m_check_version ( false )
2018-04-30 13:27:01 +00:00
, m_xml_parser ( nullptr )
2018-01-30 08:27:10 +00:00
, m_model ( nullptr )
, m_unit_factor ( 1.0f )
2018-04-30 13:27:01 +00:00
, m_curr_metadata_name ( " " )
, m_curr_characters ( " " )
2018-11-08 09:18:19 +00:00
, m_name ( " " )
2018-01-30 08:27:10 +00:00
{
}
_3MF_Importer : : ~ _3MF_Importer ( )
{
_destroy_xml_parser ( ) ;
}
2019-08-23 11:12:31 +00:00
bool _3MF_Importer : : load_model_from_file ( const std : : string & filename , Model & model , DynamicPrintConfig & config , bool check_version )
2018-01-30 08:27:10 +00:00
{
2018-04-30 13:27:01 +00:00
m_version = 0 ;
2019-08-23 11:12:31 +00:00
m_check_version = check_version ;
2018-01-30 08:27:10 +00:00
m_model = & model ;
m_unit_factor = 1.0f ;
m_curr_object . reset ( ) ;
m_objects . clear ( ) ;
m_objects_aliases . clear ( ) ;
m_instances . clear ( ) ;
2018-02-27 09:49:51 +00:00
m_geometries . clear ( ) ;
m_curr_config . object_id = - 1 ;
m_curr_config . volume_id = - 1 ;
m_objects_metadata . clear ( ) ;
2018-04-27 10:56:35 +00:00
m_layer_heights_profiles . clear ( ) ;
2019-06-11 12:39:41 +00:00
m_layer_config_ranges . clear ( ) ;
2018-10-05 08:13:21 +00:00
m_sla_support_points . clear ( ) ;
2018-04-30 13:27:01 +00:00
m_curr_metadata_name . clear ( ) ;
m_curr_characters . clear ( ) ;
2018-02-14 13:30:03 +00:00
clear_errors ( ) ;
2018-01-30 08:27:10 +00:00
2018-09-25 09:53:05 +00:00
return _load_model_from_file ( filename , model , config ) ;
2018-01-30 08:27:10 +00:00
}
void _3MF_Importer : : _destroy_xml_parser ( )
{
if ( m_xml_parser ! = nullptr )
{
XML_ParserFree ( m_xml_parser ) ;
m_xml_parser = nullptr ;
}
}
void _3MF_Importer : : _stop_xml_parser ( )
{
if ( m_xml_parser ! = nullptr )
XML_StopParser ( m_xml_parser , false ) ;
}
2018-09-25 09:53:05 +00:00
bool _3MF_Importer : : _load_model_from_file ( const std : : string & filename , Model & model , DynamicPrintConfig & config )
2018-01-30 08:27:10 +00:00
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
2019-06-02 20:27:35 +00:00
2019-06-03 13:27:46 +00:00
if ( ! open_zip_reader ( & archive , filename ) ) {
2018-02-14 13:30:03 +00:00
add_error ( " Unable to open the file " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive ) ;
mz_zip_archive_file_stat stat ;
2018-04-30 13:27:01 +00:00
2018-11-08 09:18:19 +00:00
m_name = boost : : filesystem : : path ( filename ) . filename ( ) . stem ( ) . string ( ) ;
2018-04-30 13:27:01 +00:00
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
2018-01-30 08:27:10 +00:00
for ( mz_uint i = 0 ; i < num_entries ; + + i )
{
if ( mz_zip_reader_file_stat ( & archive , i , & stat ) )
{
std : : string name ( stat . m_filename ) ;
2018-02-08 12:26:50 +00:00
std : : replace ( name . begin ( ) , name . end ( ) , ' \\ ' , ' / ' ) ;
2018-01-30 08:27:10 +00:00
2018-02-13 14:19:55 +00:00
if ( boost : : algorithm : : istarts_with ( name , MODEL_FOLDER ) & & boost : : algorithm : : iends_with ( name , MODEL_EXTENSION ) )
2018-01-30 08:27:10 +00:00
{
2019-08-23 11:12:31 +00:00
try
2018-01-30 08:27:10 +00:00
{
2019-08-23 11:12:31 +00:00
// valid model name -> extract model
if ( ! _extract_model_from_archive ( archive , stat ) )
{
close_zip_reader ( & archive ) ;
add_error ( " Archive does not contain a valid model " ) ;
return false ;
}
}
catch ( const std : : exception & e )
{
// ensure the zip archive is closed and rethrow the exception
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2019-09-02 07:38:45 +00:00
throw std : : runtime_error ( e . what ( ) ) ;
2018-01-30 08:27:10 +00:00
}
}
2018-04-30 13:27:01 +00:00
}
}
// we then loop again the entries to read other files stored in the archive
for ( mz_uint i = 0 ; i < num_entries ; + + i )
{
if ( mz_zip_reader_file_stat ( & archive , i , & stat ) )
{
std : : string name ( stat . m_filename ) ;
std : : replace ( name . begin ( ) , name . end ( ) , ' \\ ' , ' / ' ) ;
if ( boost : : algorithm : : iequals ( name , LAYER_HEIGHTS_PROFILE_FILE ) )
2018-04-27 10:56:35 +00:00
{
2019-06-11 12:39:41 +00:00
// extract slic3r layer heights profile file
2018-04-27 10:56:35 +00:00
_extract_layer_heights_profile_config_from_archive ( archive , stat ) ;
}
2019-06-11 12:39:41 +00:00
if ( boost : : algorithm : : iequals ( name , LAYER_CONFIG_RANGES_FILE ) )
{
// extract slic3r layer config ranges file
_extract_layer_config_ranges_from_archive ( archive , stat ) ;
}
2018-10-05 08:13:21 +00:00
else if ( boost : : algorithm : : iequals ( name , SLA_SUPPORT_POINTS_FILE ) )
{
// extract sla support points file
_extract_sla_support_points_from_archive ( archive , stat ) ;
}
2019-12-06 14:47:58 +00:00
else if ( boost : : algorithm : : iequals ( name , SLA_DRAIN_HOLES_FILE ) )
{
// extract sla support points file
_extract_sla_drain_holes_from_archive ( archive , stat ) ;
}
2018-02-27 09:49:51 +00:00
else if ( boost : : algorithm : : iequals ( name , PRINT_CONFIG_FILE ) )
{
// extract slic3r print config file
2018-09-25 09:53:05 +00:00
_extract_print_config_from_archive ( archive , stat , config , filename ) ;
2018-02-27 09:49:51 +00:00
}
2019-12-17 07:37:50 +00:00
if ( boost : : algorithm : : iequals ( name , CUSTOM_GCODE_PER_PRINT_Z_FILE ) )
2019-11-11 15:01:34 +00:00
{
// extract slic3r layer config ranges file
2019-12-17 07:37:50 +00:00
_extract_custom_gcode_per_print_z_from_archive ( archive , stat ) ;
2019-11-11 15:01:34 +00:00
}
2018-02-27 09:49:51 +00:00
else if ( boost : : algorithm : : iequals ( name , MODEL_CONFIG_FILE ) )
2018-02-13 14:19:55 +00:00
{
2018-02-27 09:49:51 +00:00
// extract slic3r model config file
if ( ! _extract_model_config_from_archive ( archive , stat , model ) )
2018-02-13 14:19:55 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-27 09:49:51 +00:00
add_error ( " Archive does not contain a valid model config " ) ;
2018-02-13 14:19:55 +00:00
return false ;
}
}
2018-01-30 08:27:10 +00:00
}
}
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-27 09:49:51 +00:00
for ( const IdToModelObjectMap : : value_type & object : m_objects )
{
2019-05-24 10:17:54 +00:00
ModelObject * model_object = m_model - > objects [ object . second ] ;
2018-02-27 09:49:51 +00:00
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 ;
}
2019-05-24 10:17:54 +00:00
// m_layer_heights_profiles are indexed by a 1 based model object index.
IdToLayerHeightsProfileMap : : iterator obj_layer_heights_profile = m_layer_heights_profiles . find ( object . second + 1 ) ;
2018-04-27 10:56:35 +00:00
if ( obj_layer_heights_profile ! = m_layer_heights_profiles . end ( ) )
2019-05-24 10:17:54 +00:00
model_object - > layer_height_profile = obj_layer_heights_profile - > second ;
2018-04-27 10:56:35 +00:00
2019-06-11 12:39:41 +00:00
// m_layer_config_ranges are indexed by a 1 based model object index.
IdToLayerConfigRangesMap : : iterator obj_layer_config_ranges = m_layer_config_ranges . find ( object . second + 1 ) ;
if ( obj_layer_config_ranges ! = m_layer_config_ranges . end ( ) )
model_object - > layer_config_ranges = obj_layer_config_ranges - > second ;
2019-05-24 10:17:54 +00:00
// m_sla_support_points are indexed by a 1 based model object index.
IdToSlaSupportPointsMap : : iterator obj_sla_support_points = m_sla_support_points . find ( object . second + 1 ) ;
2019-03-11 10:13:37 +00:00
if ( obj_sla_support_points ! = m_sla_support_points . end ( ) & & ! obj_sla_support_points - > second . empty ( ) ) {
2019-05-24 10:17:54 +00:00
model_object - > sla_support_points = obj_sla_support_points - > second ;
model_object - > sla_points_status = sla : : PointsStatus : : UserModified ;
2019-03-11 10:13:37 +00:00
}
2019-12-06 14:47:58 +00:00
IdToSlaDrainHolesMap : : iterator obj_drain_holes = m_sla_drain_holes . find ( object . second + 1 ) ;
if ( obj_drain_holes ! = m_sla_drain_holes . end ( ) & & ! obj_drain_holes - > second . empty ( ) ) {
model_object - > sla_drain_holes = obj_drain_holes - > second ;
}
2018-10-05 08:13:21 +00:00
2018-02-27 09:49:51 +00:00
IdToMetadataMap : : iterator obj_metadata = m_objects_metadata . find ( object . first ) ;
if ( obj_metadata ! = m_objects_metadata . end ( ) )
{
// config data has been found, this model was saved using slic3r pe
2018-02-27 14:46:54 +00:00
// apply object's name and config data
2018-02-27 09:49:51 +00:00
for ( const Metadata & metadata : obj_metadata - > second . metadata )
{
2018-02-27 14:46:54 +00:00
if ( metadata . key = = " name " )
2019-05-24 10:17:54 +00:00
model_object - > name = metadata . value ;
2018-02-27 14:46:54 +00:00
else
2019-05-24 10:17:54 +00:00
model_object - > config . set_deserialize ( metadata . key , metadata . value ) ;
2018-02-27 09:49:51 +00:00
}
// select object's detected volumes
volumes_ptr = & obj_metadata - > second . volumes ;
}
else
{
// config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate
volumes . emplace_back ( 0 , ( int ) obj_geometry - > second . triangles . size ( ) / 3 - 1 ) ;
// select as volumes
volumes_ptr = & volumes ;
}
2019-05-24 10:17:54 +00:00
if ( ! _generate_volumes ( * model_object , obj_geometry - > second , * volumes_ptr ) )
2018-02-27 09:49:51 +00:00
return false ;
}
2020-01-06 11:10:57 +00:00
// // fixes the min z of the model if negative
// model.adjust_min_z();
2018-03-06 09:26:39 +00:00
2018-01-30 08:27:10 +00:00
return true ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Importer : : _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
2018-01-30 08:27:10 +00:00
{
2018-02-14 13:30:03 +00:00
if ( stat . m_uncomp_size = = 0 )
{
add_error ( " Found invalid size " ) ;
return false ;
}
2018-01-30 08:27:10 +00:00
_destroy_xml_parser ( ) ;
m_xml_parser = XML_ParserCreate ( nullptr ) ;
if ( m_xml_parser = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to create parser " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
XML_SetUserData ( m_xml_parser , ( void * ) this ) ;
2018-02-27 09:49:51 +00:00
XML_SetElementHandler ( m_xml_parser , _3MF_Importer : : _handle_start_model_xml_element , _3MF_Importer : : _handle_end_model_xml_element ) ;
2018-04-30 13:27:01 +00:00
XML_SetCharacterDataHandler ( m_xml_parser , _3MF_Importer : : _handle_model_xml_characters ) ;
2018-01-30 08:27:10 +00:00
2019-08-28 14:03:26 +00:00
struct CallbackData
2018-01-30 08:27:10 +00:00
{
2019-08-28 14:03:26 +00:00
XML_Parser & parser ;
const mz_zip_archive_file_stat & stat ;
2018-01-30 08:27:10 +00:00
2019-08-28 14:03:26 +00:00
CallbackData ( XML_Parser & parser , const mz_zip_archive_file_stat & stat ) : parser ( parser ) , stat ( stat ) { }
} ;
CallbackData data ( m_xml_parser , stat ) ;
mz_bool res = 0 ;
try
2018-01-30 08:27:10 +00:00
{
2019-08-28 14:03:26 +00:00
res = mz_zip_reader_extract_file_to_callback ( & archive , stat . m_filename , [ ] ( void * pOpaque , mz_uint64 file_ofs , const void * pBuf , size_t n ) - > size_t {
CallbackData * data = ( CallbackData * ) pOpaque ;
if ( ! XML_Parse ( data - > parser , ( const char * ) pBuf , ( int ) n , ( file_ofs + n = = data - > stat . m_uncomp_size ) ? 1 : 0 ) )
{
char error_buf [ 1024 ] ;
: : sprintf ( error_buf , " Error (%s) while parsing '%s' at line %d " , XML_ErrorString ( XML_GetErrorCode ( data - > parser ) ) , data - > stat . m_filename , ( int ) XML_GetCurrentLineNumber ( data - > parser ) ) ;
throw std : : runtime_error ( error_buf ) ;
}
return n ;
} , & data , 0 ) ;
}
2019-09-02 09:55:14 +00:00
catch ( const version_error & e )
{
// rethrow the exception
throw std : : runtime_error ( e . what ( ) ) ;
}
2019-08-28 14:03:26 +00:00
catch ( std : : exception & e )
{
add_error ( e . what ( ) ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
2019-08-28 14:03:26 +00:00
if ( res = = 0 )
2018-01-30 08:27:10 +00:00
{
2019-08-28 14:03:26 +00:00
add_error ( " Error while extracting model data from zip archive " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
return true ;
}
2018-09-25 09:53:05 +00: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 14:19:55 +00:00
{
if ( stat . m_uncomp_size > 0 )
{
2018-04-27 10:56:35 +00:00
std : : string buffer ( ( size_t ) stat . m_uncomp_size , 0 ) ;
2018-02-13 14:19:55 +00:00
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , ( void * ) buffer . data ( ) , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " Error while reading config data to buffer " ) ;
2018-04-27 10:56:35 +00:00
return ;
2018-02-13 14:19:55 +00:00
}
2018-09-25 09:53:05 +00:00
config . load_from_gcode_string ( buffer . data ( ) ) ;
2018-02-13 14:19:55 +00:00
}
2018-04-27 10:56:35 +00:00
}
2018-02-13 14:19:55 +00:00
2018-04-27 10:56:35 +00:00
void _3MF_Importer : : _extract_layer_heights_profile_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
{
if ( stat . m_uncomp_size > 0 )
{
std : : string buffer ( ( size_t ) stat . m_uncomp_size , 0 ) ;
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , ( void * ) buffer . data ( ) , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
add_error ( " Error while reading layer heights profile data to buffer " ) ;
return ;
}
if ( buffer . back ( ) = = ' \n ' )
buffer . pop_back ( ) ;
std : : vector < std : : string > objects ;
boost : : split ( objects , buffer , boost : : is_any_of ( " \n " ) , boost : : token_compress_off ) ;
for ( const std : : string & object : objects )
{
std : : vector < std : : string > object_data ;
boost : : split ( object_data , object , boost : : is_any_of ( " | " ) , boost : : token_compress_off ) ;
if ( object_data . size ( ) ! = 2 )
{
add_error ( " Error while reading object data " ) ;
continue ;
}
std : : vector < std : : string > object_data_id ;
boost : : split ( object_data_id , object_data [ 0 ] , boost : : is_any_of ( " = " ) , boost : : token_compress_off ) ;
if ( object_data_id . size ( ) ! = 2 )
{
add_error ( " Error while reading object id " ) ;
continue ;
}
int object_id = std : : atoi ( object_data_id [ 1 ] . c_str ( ) ) ;
if ( object_id = = 0 )
{
add_error ( " Found invalid object id " ) ;
continue ;
}
IdToLayerHeightsProfileMap : : iterator object_item = m_layer_heights_profiles . find ( object_id ) ;
if ( object_item ! = m_layer_heights_profiles . end ( ) )
{
add_error ( " Found duplicated layer heights profile " ) ;
continue ;
}
std : : vector < std : : string > object_data_profile ;
boost : : split ( object_data_profile , object_data [ 1 ] , boost : : is_any_of ( " ; " ) , boost : : token_compress_off ) ;
if ( ( object_data_profile . size ( ) < = 4 ) | | ( object_data_profile . size ( ) % 2 ! = 0 ) )
{
add_error ( " Found invalid layer heights profile " ) ;
continue ;
}
std : : vector < coordf_t > profile ;
profile . reserve ( object_data_profile . size ( ) ) ;
for ( const std : : string & value : object_data_profile )
{
profile . push_back ( ( coordf_t ) std : : atof ( value . c_str ( ) ) ) ;
}
m_layer_heights_profiles . insert ( IdToLayerHeightsProfileMap : : value_type ( object_id , profile ) ) ;
}
}
2018-02-13 14:19:55 +00:00
}
2019-06-11 12:39:41 +00:00
void _3MF_Importer : : _extract_layer_config_ranges_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 config ranges data to buffer " ) ;
return ;
}
2019-06-27 11:54:51 +00:00
std : : istringstream iss ( buffer ) ; // wrap returned xml to istringstream
pt : : ptree objects_tree ;
pt : : read_xml ( iss , objects_tree ) ;
2019-06-11 12:39:41 +00:00
2019-06-27 11:54:51 +00:00
for ( const auto & object : objects_tree . get_child ( " objects " ) )
2019-06-11 12:39:41 +00:00
{
2019-06-27 11:54:51 +00:00
pt : : ptree object_tree = object . second ;
int obj_idx = object_tree . get < int > ( " <xmlattr>.id " , - 1 ) ;
if ( obj_idx < = 0 ) {
2019-06-11 12:39:41 +00:00
add_error ( " Found invalid object id " ) ;
continue ;
}
2019-06-27 11:54:51 +00:00
IdToLayerConfigRangesMap : : iterator object_item = m_layer_config_ranges . find ( obj_idx ) ;
2019-06-11 12:39:41 +00:00
if ( object_item ! = m_layer_config_ranges . end ( ) ) {
add_error ( " Found duplicated layer config range " ) ;
continue ;
}
t_layer_config_ranges config_ranges ;
2019-06-27 13:57:29 +00:00
for ( const auto & range : object_tree )
2019-06-11 12:39:41 +00:00
{
2019-06-27 13:57:29 +00:00
if ( range . first ! = " range " )
continue ;
2019-06-27 11:54:51 +00:00
pt : : ptree range_tree = range . second ;
double min_z = range_tree . get < double > ( " <xmlattr>.min_z " ) ;
double max_z = range_tree . get < double > ( " <xmlattr>.max_z " ) ;
2019-06-11 12:39:41 +00:00
// get Z range information
2019-06-27 11:54:51 +00:00
DynamicPrintConfig & config = config_ranges [ { min_z , max_z } ] ;
2019-06-11 12:39:41 +00:00
2019-06-27 13:57:29 +00:00
for ( const auto & option : range_tree )
2019-06-11 12:39:41 +00:00
{
2019-06-27 13:57:29 +00:00
if ( option . first ! = " option " )
continue ;
2019-06-27 11:54:51 +00:00
std : : string opt_key = option . second . get < std : : string > ( " <xmlattr>.opt_key " ) ;
std : : string value = option . second . data ( ) ;
config . set_deserialize ( opt_key , value ) ;
2019-06-11 12:39:41 +00:00
}
}
if ( ! config_ranges . empty ( ) )
2019-06-27 11:54:51 +00:00
m_layer_config_ranges . insert ( IdToLayerConfigRangesMap : : value_type ( obj_idx , config_ranges ) ) ;
2019-06-11 12:39:41 +00:00
}
}
}
2018-10-05 08:13:21 +00: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 ) ;
2019-02-01 15:12:00 +00:00
// Info on format versioning - see 3mf.hpp
int version = 0 ;
2019-12-06 14:47:58 +00:00
std : : string key ( " support_points_format_version= " ) ;
if ( ! objects . empty ( ) & & objects [ 0 ] . find ( key ) ! = std : : string : : npos ) {
objects [ 0 ] . erase ( objects [ 0 ] . begin ( ) , objects [ 0 ] . begin ( ) + long ( key . size ( ) ) ) ; // removes the string
2019-02-01 15:12:00 +00:00
version = std : : stoi ( objects [ 0 ] ) ;
objects . erase ( objects . begin ( ) ) ; // pop the header
}
2018-10-05 08:13:21 +00:00
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 ) ;
2019-02-01 15:12:00 +00:00
2018-10-05 08:13:21 +00:00
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 ) ;
2019-01-30 07:26:23 +00:00
std : : vector < sla : : SupportPoint > sla_support_points ;
2018-10-05 08:13:21 +00:00
2019-02-01 15:12:00 +00:00
if ( version = = 0 ) {
for ( unsigned int i = 0 ; i < object_data_points . size ( ) ; i + = 3 )
2019-05-24 10:17:54 +00:00
sla_support_points . emplace_back ( float ( std : : atof ( object_data_points [ i + 0 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 1 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 2 ] . c_str ( ) ) ) ,
2019-02-01 15:12:00 +00:00
0.4f ,
false ) ;
}
if ( version = = 1 ) {
for ( unsigned int i = 0 ; i < object_data_points . size ( ) ; i + = 5 )
2019-05-24 10:17:54 +00:00
sla_support_points . emplace_back ( float ( std : : atof ( object_data_points [ i + 0 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 1 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 2 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 3 ] . c_str ( ) ) ) ,
//FIXME storing boolean as 0 / 1 and importing it as float.
std : : abs ( std : : atof ( object_data_points [ i + 4 ] . c_str ( ) ) - 1. ) < EPSILON ) ;
2019-02-01 15:12:00 +00:00
}
2018-10-05 08:13:21 +00:00
if ( ! sla_support_points . empty ( ) )
m_sla_support_points . insert ( IdToSlaSupportPointsMap : : value_type ( object_id , sla_support_points ) ) ;
}
}
}
2019-12-06 14:47:58 +00:00
void _3MF_Importer : : _extract_sla_drain_holes_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 ) ;
// Info on format versioning - see 3mf.hpp
int version = 0 ;
std : : string key ( " drain_holes_format_version= " ) ;
if ( ! objects . empty ( ) & & objects [ 0 ] . find ( key ) ! = std : : string : : npos ) {
objects [ 0 ] . erase ( objects [ 0 ] . begin ( ) , objects [ 0 ] . begin ( ) + long ( key . size ( ) ) ) ; // removes the string
version = std : : stoi ( objects [ 0 ] ) ;
objects . erase ( objects . begin ( ) ) ; // pop the header
}
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 ;
}
IdToSlaDrainHolesMap : : iterator object_item = m_sla_drain_holes . find ( object_id ) ;
if ( object_item ! = m_sla_drain_holes . end ( ) )
{
add_error ( " Found duplicated SLA drain holes " ) ;
continue ;
}
std : : vector < std : : string > object_data_points ;
boost : : split ( object_data_points , object_data [ 1 ] , boost : : is_any_of ( " " ) , boost : : token_compress_off ) ;
sla : : DrainHoles sla_drain_holes ;
if ( version = = 1 ) {
for ( unsigned int i = 0 ; i < object_data_points . size ( ) ; i + = 8 )
sla_drain_holes . emplace_back ( Vec3f { float ( std : : atof ( object_data_points [ i + 0 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 1 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 2 ] . c_str ( ) ) ) } ,
Vec3f { float ( std : : atof ( object_data_points [ i + 3 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 4 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 5 ] . c_str ( ) ) ) } ,
float ( std : : atof ( object_data_points [ i + 6 ] . c_str ( ) ) ) ,
float ( std : : atof ( object_data_points [ i + 7 ] . c_str ( ) ) ) ) ;
}
if ( ! sla_drain_holes . empty ( ) )
m_sla_drain_holes . insert ( IdToSlaDrainHolesMap : : value_type ( object_id , sla_drain_holes ) ) ;
}
}
}
2018-10-05 08:13:21 +00:00
2018-02-27 09:49:51 +00:00
bool _3MF_Importer : : _extract_model_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model )
{
if ( stat . m_uncomp_size = = 0 )
{
add_error ( " Found invalid size " ) ;
return false ;
}
_destroy_xml_parser ( ) ;
m_xml_parser = XML_ParserCreate ( nullptr ) ;
if ( m_xml_parser = = nullptr )
{
add_error ( " Unable to create parser " ) ;
return false ;
}
XML_SetUserData ( m_xml_parser , ( void * ) this ) ;
XML_SetElementHandler ( m_xml_parser , _3MF_Importer : : _handle_start_config_xml_element , _3MF_Importer : : _handle_end_config_xml_element ) ;
void * parser_buffer = XML_GetBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size ) ;
if ( parser_buffer = = nullptr )
{
add_error ( " Unable to create buffer " ) ;
return false ;
}
mz_bool res = mz_zip_reader_extract_file_to_mem ( & archive , stat . m_filename , parser_buffer , ( size_t ) stat . m_uncomp_size , 0 ) ;
if ( res = = 0 )
{
add_error ( " Error while reading config data to buffer " ) ;
return false ;
}
if ( ! XML_ParseBuffer ( m_xml_parser , ( int ) stat . m_uncomp_size , 1 ) )
{
char error_buf [ 1024 ] ;
2019-06-25 11:06:04 +00:00
: : sprintf ( error_buf , " Error (%s) while parsing xml file at line %d " , XML_ErrorString ( XML_GetErrorCode ( m_xml_parser ) ) , ( int ) XML_GetCurrentLineNumber ( m_xml_parser ) ) ;
2018-02-27 09:49:51 +00:00
add_error ( error_buf ) ;
return false ;
}
return true ;
}
2019-12-17 07:37:50 +00:00
void _3MF_Importer : : _extract_custom_gcode_per_print_z_from_archive ( : : mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
2019-11-11 15:01:34 +00:00
{
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 custom Gcodes per height data to buffer " ) ;
return ;
}
std : : istringstream iss ( buffer ) ; // wrap returned xml to istringstream
pt : : ptree main_tree ;
pt : : read_xml ( iss , main_tree ) ;
2019-12-17 07:37:50 +00:00
if ( main_tree . front ( ) . first ! = " custom_gcodes_per_print_z " )
2019-11-11 15:01:34 +00:00
return ;
pt : : ptree code_tree = main_tree . front ( ) . second ;
2020-01-16 15:01:19 +00:00
m_model - > custom_gcode_per_print_z . gcodes . clear ( ) ;
2019-11-11 15:01:34 +00:00
for ( const auto & code : code_tree )
{
2020-01-23 11:49:39 +00:00
if ( code . first = = " mode " )
{
pt : : ptree tree = code . second ;
std : : string mode = tree . get < std : : string > ( " <xmlattr>.value " ) ;
m_model - > custom_gcode_per_print_z . mode = mode = = CustomGCode : : SingleExtruderMode ? CustomGCode : : Mode : : SingleExtruder :
mode = = CustomGCode : : MultiAsSingleMode ? CustomGCode : : Mode : : MultiAsSingle :
CustomGCode : : Mode : : MultiExtruder ;
}
2019-11-11 15:01:34 +00:00
if ( code . first ! = " code " )
continue ;
2020-06-08 10:25:29 +00:00
2019-11-11 15:01:34 +00:00
pt : : ptree tree = code . second ;
2020-06-08 10:25:29 +00:00
double print_z = tree . get < double > ( " <xmlattr>.print_z " ) ;
int extruder = tree . get < int > ( " <xmlattr>.extruder " ) ;
std : : string color = tree . get < std : : string > ( " <xmlattr>.color " ) ;
2019-11-11 15:01:34 +00:00
2020-06-08 10:25:29 +00:00
CustomGCode : : Type type ;
std : : string extra ;
if ( tree . find ( " type " ) = = tree . not_found ( ) )
{
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
// read old data ...
std : : string gcode = tree . get < std : : string > ( " <xmlattr>.gcode " ) ;
// ... and interpret them to the new data
type = gcode = = " M600 " ? CustomGCode : : ColorChange :
gcode = = " M601 " ? CustomGCode : : PausePrint :
gcode = = " tool_change " ? CustomGCode : : ToolChange : CustomGCode : : Custom ;
extra = type = = CustomGCode : : PausePrint ? color :
type = = CustomGCode : : Custom ? gcode : " " ;
}
else
{
type = static_cast < CustomGCode : : Type > ( tree . get < int > ( " <xmlattr>.type " ) ) ;
extra = tree . get < std : : string > ( " <xmlattr>.extra " ) ;
}
m_model - > custom_gcode_per_print_z . gcodes . push_back ( CustomGCode : : Item { print_z , type , extruder , color , extra } ) ;
2019-11-11 15:01:34 +00:00
}
}
}
2018-02-27 09:49:51 +00:00
void _3MF_Importer : : _handle_start_model_xml_element ( const char * name , const char * * attributes )
2018-01-30 08:27:10 +00:00
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
unsigned int num_attributes = ( unsigned int ) XML_GetSpecifiedAttributeCount ( m_xml_parser ) ;
if ( : : strcmp ( MODEL_TAG , name ) = = 0 )
res = _handle_start_model ( attributes , num_attributes ) ;
else if ( : : strcmp ( RESOURCES_TAG , name ) = = 0 )
res = _handle_start_resources ( attributes , num_attributes ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_start_object ( attributes , num_attributes ) ;
else if ( : : strcmp ( MESH_TAG , name ) = = 0 )
res = _handle_start_mesh ( attributes , num_attributes ) ;
else if ( : : strcmp ( VERTICES_TAG , name ) = = 0 )
res = _handle_start_vertices ( attributes , num_attributes ) ;
else if ( : : strcmp ( VERTEX_TAG , name ) = = 0 )
res = _handle_start_vertex ( attributes , num_attributes ) ;
else if ( : : strcmp ( TRIANGLES_TAG , name ) = = 0 )
res = _handle_start_triangles ( attributes , num_attributes ) ;
else if ( : : strcmp ( TRIANGLE_TAG , name ) = = 0 )
res = _handle_start_triangle ( attributes , num_attributes ) ;
else if ( : : strcmp ( COMPONENTS_TAG , name ) = = 0 )
res = _handle_start_components ( attributes , num_attributes ) ;
else if ( : : strcmp ( COMPONENT_TAG , name ) = = 0 )
res = _handle_start_component ( attributes , num_attributes ) ;
else if ( : : strcmp ( BUILD_TAG , name ) = = 0 )
res = _handle_start_build ( attributes , num_attributes ) ;
else if ( : : strcmp ( ITEM_TAG , name ) = = 0 )
res = _handle_start_item ( attributes , num_attributes ) ;
2018-04-30 13:27:01 +00:00
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_start_metadata ( attributes , num_attributes ) ;
2018-01-30 08:27:10 +00:00
if ( ! res )
_stop_xml_parser ( ) ;
}
2018-02-27 09:49:51 +00:00
void _3MF_Importer : : _handle_end_model_xml_element ( const char * name )
2018-01-30 08:27:10 +00:00
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
if ( : : strcmp ( MODEL_TAG , name ) = = 0 )
res = _handle_end_model ( ) ;
else if ( : : strcmp ( RESOURCES_TAG , name ) = = 0 )
res = _handle_end_resources ( ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_end_object ( ) ;
else if ( : : strcmp ( MESH_TAG , name ) = = 0 )
res = _handle_end_mesh ( ) ;
else if ( : : strcmp ( VERTICES_TAG , name ) = = 0 )
res = _handle_end_vertices ( ) ;
else if ( : : strcmp ( VERTEX_TAG , name ) = = 0 )
res = _handle_end_vertex ( ) ;
else if ( : : strcmp ( TRIANGLES_TAG , name ) = = 0 )
res = _handle_end_triangles ( ) ;
else if ( : : strcmp ( TRIANGLE_TAG , name ) = = 0 )
res = _handle_end_triangle ( ) ;
else if ( : : strcmp ( COMPONENTS_TAG , name ) = = 0 )
res = _handle_end_components ( ) ;
else if ( : : strcmp ( COMPONENT_TAG , name ) = = 0 )
res = _handle_end_component ( ) ;
else if ( : : strcmp ( BUILD_TAG , name ) = = 0 )
res = _handle_end_build ( ) ;
else if ( : : strcmp ( ITEM_TAG , name ) = = 0 )
res = _handle_end_item ( ) ;
2018-04-30 13:27:01 +00:00
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_end_metadata ( ) ;
2018-01-30 08:27:10 +00:00
if ( ! res )
_stop_xml_parser ( ) ;
}
2018-04-30 13:27:01 +00:00
void _3MF_Importer : : _handle_model_xml_characters ( const XML_Char * s , int len )
{
m_curr_characters . append ( s , len ) ;
}
2018-02-27 09:49:51 +00:00
void _3MF_Importer : : _handle_start_config_xml_element ( const char * name , const char * * attributes )
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
unsigned int num_attributes = ( unsigned int ) XML_GetSpecifiedAttributeCount ( m_xml_parser ) ;
if ( : : strcmp ( CONFIG_TAG , name ) = = 0 )
res = _handle_start_config ( attributes , num_attributes ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_start_config_object ( attributes , num_attributes ) ;
else if ( : : strcmp ( VOLUME_TAG , name ) = = 0 )
res = _handle_start_config_volume ( attributes , num_attributes ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_start_config_metadata ( attributes , num_attributes ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
void _3MF_Importer : : _handle_end_config_xml_element ( const char * name )
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
if ( : : strcmp ( CONFIG_TAG , name ) = = 0 )
res = _handle_end_config ( ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_end_config_object ( ) ;
else if ( : : strcmp ( VOLUME_TAG , name ) = = 0 )
res = _handle_end_config_volume ( ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_end_config_metadata ( ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
2018-01-30 08:27:10 +00:00
bool _3MF_Importer : : _handle_start_model ( const char * * attributes , unsigned int num_attributes )
{
m_unit_factor = get_unit_factor ( get_attribute_value_string ( attributes , num_attributes , UNIT_ATTR ) ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_model ( )
{
// deletes all non-built or non-instanced objects
for ( const IdToModelObjectMap : : value_type & object : m_objects )
{
2019-05-24 10:17:54 +00:00
ModelObject * model_object = m_model - > objects [ object . second ] ;
if ( ( model_object ! = nullptr ) & & ( model_object - > instances . size ( ) = = 0 ) )
m_model - > delete_object ( model_object ) ;
2018-01-30 08:27:10 +00:00
}
// applies instances' matrices
for ( Instance & instance : m_instances )
{
if ( instance . instance ! = nullptr )
{
ModelObject * object = instance . instance - > get_object ( ) ;
2018-04-12 08:10:15 +00:00
if ( object ! = nullptr )
2018-01-30 08:27:10 +00:00
{
2018-08-28 12:30:36 +00:00
// apply the transform to the instance
_apply_transform ( * instance . instance , instance . transform ) ;
2018-01-30 08:27:10 +00:00
}
}
}
return true ;
}
bool _3MF_Importer : : _handle_start_resources ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_end_resources ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_object ( const char * * attributes , unsigned int num_attributes )
{
// reset current data
m_curr_object . reset ( ) ;
if ( is_valid_object_type ( get_attribute_value_string ( attributes , num_attributes , TYPE_ATTR ) ) )
{
// create new object (it may be removed later if no instances are generated from it)
2019-05-24 10:17:54 +00:00
m_curr_object . model_object_idx = ( int ) m_model - > objects . size ( ) ;
2018-01-30 08:27:10 +00:00
m_curr_object . object = m_model - > add_object ( ) ;
if ( m_curr_object . object = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to create object " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
// set object data
m_curr_object . object - > name = get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
2018-11-08 09:18:19 +00:00
if ( m_curr_object . object - > name . empty ( ) )
m_curr_object . object - > name = m_name + " _ " + std : : to_string ( m_model - > objects . size ( ) ) ;
2018-01-30 08:27:10 +00:00
m_curr_object . id = get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
}
return true ;
}
bool _3MF_Importer : : _handle_end_object ( )
{
if ( m_curr_object . object ! = nullptr )
{
if ( m_curr_object . geometry . empty ( ) )
{
// no geometry defined
// remove the object from the model
m_model - > delete_object ( m_curr_object . object ) ;
if ( m_curr_object . components . empty ( ) )
{
// no components defined -> invalid object, delete it
IdToModelObjectMap : : iterator object_item = m_objects . find ( m_curr_object . id ) ;
if ( object_item ! = m_objects . end ( ) )
m_objects . erase ( object_item ) ;
IdToAliasesMap : : iterator alias_item = m_objects_aliases . find ( m_curr_object . id ) ;
if ( alias_item ! = m_objects_aliases . end ( ) )
m_objects_aliases . erase ( alias_item ) ;
}
else
// adds components to aliases
m_objects_aliases . insert ( IdToAliasesMap : : value_type ( m_curr_object . id , m_curr_object . components ) ) ;
}
else
{
2018-02-27 09:49:51 +00:00
// geometry defined, store it for later use
m_geometries . insert ( IdToGeometryMap : : value_type ( m_curr_object . id , std : : move ( m_curr_object . geometry ) ) ) ;
2018-01-30 08:27:10 +00:00
// stores the object for later use
if ( m_objects . find ( m_curr_object . id ) = = m_objects . end ( ) )
{
2019-05-24 10:17:54 +00:00
m_objects . insert ( IdToModelObjectMap : : value_type ( m_curr_object . id , m_curr_object . model_object_idx ) ) ;
2018-01-30 08:27:10 +00:00
m_objects_aliases . insert ( IdToAliasesMap : : value_type ( m_curr_object . id , ComponentsList ( 1 , Component ( m_curr_object . id ) ) ) ) ; // aliases itself
}
else
{
2018-02-14 13:30:03 +00:00
add_error ( " Found object with duplicate id " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
}
}
return true ;
}
bool _3MF_Importer : : _handle_start_mesh ( const char * * attributes , unsigned int num_attributes )
{
// reset current geometry
m_curr_object . geometry . reset ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_mesh ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_vertices ( const char * * attributes , unsigned int num_attributes )
{
// reset current vertices
m_curr_object . geometry . vertices . clear ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_vertices ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_vertex ( const char * * attributes , unsigned int num_attributes )
{
// appends the vertex coordinates
// missing values are set equal to ZERO
m_curr_object . geometry . vertices . push_back ( m_unit_factor * get_attribute_value_float ( attributes , num_attributes , X_ATTR ) ) ;
m_curr_object . geometry . vertices . push_back ( m_unit_factor * get_attribute_value_float ( attributes , num_attributes , Y_ATTR ) ) ;
m_curr_object . geometry . vertices . push_back ( m_unit_factor * get_attribute_value_float ( attributes , num_attributes , Z_ATTR ) ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_vertex ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_triangles ( const char * * attributes , unsigned int num_attributes )
{
// reset current triangles
m_curr_object . geometry . triangles . clear ( ) ;
return true ;
}
bool _3MF_Importer : : _handle_end_triangles ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_triangle ( const char * * attributes , unsigned int num_attributes )
{
// we are ignoring the following attributes:
// p1
// p2
// p3
// pid
// see specifications
// appends the triangle's vertices indices
// missing values are set equal to ZERO
m_curr_object . geometry . triangles . push_back ( ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , V1_ATTR ) ) ;
m_curr_object . geometry . triangles . push_back ( ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , V2_ATTR ) ) ;
m_curr_object . geometry . triangles . push_back ( ( unsigned int ) get_attribute_value_int ( attributes , num_attributes , V3_ATTR ) ) ;
2020-07-24 14:53:05 +00:00
m_curr_object . geometry . custom_supports . push_back ( get_attribute_value_string ( attributes , num_attributes , CUSTOM_SUPPORTS_ATTR ) ) ;
2018-01-30 08:27:10 +00:00
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 ) ;
2019-09-19 07:09:11 +00:00
Transform3d transform = get_transform_from_3mf_specs_string ( get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
2018-01-30 08:27:10 +00: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 13:30:03 +00:00
add_error ( " Found component with invalid object id " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
}
2018-08-28 12:30:36 +00:00
m_curr_object . components . emplace_back ( object_id , transform ) ;
2018-01-30 08:27:10 +00: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 ) ;
2019-09-19 07:09:11 +00:00
Transform3d transform = get_transform_from_3mf_specs_string ( get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
2019-08-06 12:54:38 +00:00
int printable = get_attribute_value_bool ( attributes , num_attributes , PRINTABLE_ATTR ) ;
2018-01-30 08:27:10 +00:00
2019-08-06 12:54:38 +00:00
return _create_object_instance ( object_id , transform , printable , 1 ) ;
2018-01-30 08:27:10 +00:00
}
bool _3MF_Importer : : _handle_end_item ( )
{
// do nothing
return true ;
}
2018-04-30 13:27:01 +00:00
bool _3MF_Importer : : _handle_start_metadata ( const char * * attributes , unsigned int num_attributes )
{
m_curr_characters . clear ( ) ;
std : : string name = get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
if ( ! name . empty ( ) )
m_curr_metadata_name = name ;
return true ;
}
bool _3MF_Importer : : _handle_end_metadata ( )
{
if ( m_curr_metadata_name = = SLIC3RPE_3MF_VERSION )
2019-08-23 11:12:31 +00:00
{
2018-04-30 13:27:01 +00:00
m_version = ( unsigned int ) atoi ( m_curr_characters . c_str ( ) ) ;
2019-12-19 09:22:46 +00:00
if ( m_check_version & & ( m_version > VERSION_3MF_COMPATIBLE ) )
2019-08-23 11:12:31 +00:00
{
2019-12-06 07:59:25 +00:00
// std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."));
// throw version_error(msg.c_str());
const std : : string msg = ( boost : : format ( _ ( L ( " The selected 3mf file has been saved with a newer version of %1% and is not compatible. " ) ) ) % std : : string ( SLIC3R_APP_NAME ) ) . str ( ) ;
throw version_error ( msg ) ;
2019-08-23 11:12:31 +00:00
}
}
2018-04-30 13:27:01 +00:00
return true ;
}
2019-08-06 12:54:38 +00:00
bool _3MF_Importer : : _create_object_instance ( int object_id , const Transform3d & transform , const bool printable , unsigned int recur_counter )
2018-01-30 08:27:10 +00:00
{
static const unsigned int MAX_RECURSIONS = 10 ;
// escape from circular aliasing
if ( recur_counter > MAX_RECURSIONS )
{
2018-02-14 13:30:03 +00:00
add_error ( " Too many recursions " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
IdToAliasesMap : : iterator it = m_objects_aliases . find ( object_id ) ;
if ( it = = m_objects_aliases . end ( ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Found item with invalid object id " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
if ( ( it - > second . size ( ) = = 1 ) & & ( it - > second [ 0 ] . object_id = = object_id ) )
{
// aliasing to itself
IdToModelObjectMap : : iterator object_item = m_objects . find ( object_id ) ;
2019-05-24 10:17:54 +00:00
if ( ( object_item = = m_objects . end ( ) ) | | ( object_item - > second = = - 1 ) )
2018-01-30 08:27:10 +00:00
{
2018-02-14 13:30:03 +00:00
add_error ( " Found invalid object " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
else
{
2019-05-24 10:17:54 +00:00
ModelInstance * instance = m_model - > objects [ object_item - > second ] - > add_instance ( ) ;
2018-01-30 08:27:10 +00:00
if ( instance = = nullptr )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add object instance " ) ;
2018-01-30 08:27:10 +00:00
return false ;
}
2019-08-06 12:54:38 +00:00
instance - > printable = printable ;
2018-01-30 08:27:10 +00:00
2018-08-28 12:30:36 +00:00
m_instances . emplace_back ( instance , transform ) ;
2018-01-30 08:27:10 +00:00
}
}
else
{
// recursively process nested components
for ( const Component & component : it - > second )
{
2019-08-06 12:54:38 +00:00
if ( ! _create_object_instance ( component . object_id , transform * component . transform , printable , recur_counter + 1 ) )
2018-01-30 08:27:10 +00:00
return false ;
}
}
return true ;
}
2018-08-28 12:30:36 +00:00
void _3MF_Importer : : _apply_transform ( ModelInstance & instance , const Transform3d & transform )
2018-01-30 08:27:10 +00:00
{
2018-11-01 12:42:07 +00:00
Slic3r : : Geometry : : Transformation t ( transform ) ;
// invalid scale value, return
if ( ! t . get_scaling_factor ( ) . all ( ) )
return ;
instance . set_transformation ( t ) ;
2018-01-30 08:27:10 +00:00
}
2018-02-27 09:49:51 +00:00
bool _3MF_Importer : : _handle_start_config ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_end_config ( )
{
// do nothing
return true ;
}
bool _3MF_Importer : : _handle_start_config_object ( const char * * attributes , unsigned int num_attributes )
{
int object_id = get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
IdToMetadataMap : : iterator object_item = m_objects_metadata . find ( object_id ) ;
if ( object_item ! = m_objects_metadata . end ( ) )
{
add_error ( " Found duplicated object id " ) ;
return false ;
}
2020-01-02 10:37:50 +00:00
// Added because of github #3435, currently not used by PrusaSlicer
2020-03-03 13:52:16 +00:00
// int instances_count_id = get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
2020-01-02 10:37:50 +00:00
2018-02-27 09:49:51 +00:00
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 13:17:36 +00:00
m_curr_config . volume_id = ( int ) object - > second . volumes . size ( ) ;
2018-02-27 09:49:51 +00: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 )
{
2019-06-25 11:06:04 +00:00
if ( size_t ( m_curr_config . volume_id ) < object - > second . volumes . size ( ) )
2018-02-27 09:49:51 +00:00
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 13:17:36 +00:00
unsigned int geo_tri_count = ( unsigned int ) geometry . triangles . size ( ) / 3 ;
2018-02-27 09:49:51 +00: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 ;
}
2019-12-19 09:22:46 +00:00
Transform3d volume_matrix_to_object = Transform3d : : Identity ( ) ;
bool has_transform = false ;
// extract the volume transformation from the volume's metadata, if present
for ( const Metadata & metadata : volume_data . metadata )
2019-09-23 10:33:21 +00:00
{
2019-12-19 09:22:46 +00:00
if ( metadata . key = = MATRIX_KEY )
2019-09-23 10:33:21 +00:00
{
2019-12-19 09:22:46 +00:00
volume_matrix_to_object = Slic3r : : Geometry : : transform3d_from_string ( metadata . value ) ;
has_transform = ! volume_matrix_to_object . isApprox ( Transform3d : : Identity ( ) , 1e-10 ) ;
break ;
2019-09-23 10:33:21 +00:00
}
}
2018-02-27 09:49:51 +00:00
// splits volume out of imported geometry
2019-06-11 15:08:47 +00:00
TriangleMesh triangle_mesh ;
stl_file & stl = triangle_mesh . stl ;
unsigned int triangles_count = volume_data . last_triangle_id - volume_data . first_triangle_id + 1 ;
stl . stats . type = inmemory ;
2018-02-27 09:49:51 +00:00
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 13:17:36 +00:00
for ( unsigned int i = 0 ; i < triangles_count ; + + i )
2018-02-27 09:49:51 +00:00
{
unsigned int ii = i * 3 ;
stl_facet & facet = stl . facet_start [ i ] ;
for ( unsigned int v = 0 ; v < 3 ; + + v )
{
2019-09-23 10:33:21 +00:00
unsigned int tri_id = geometry . triangles [ src_start_id + ii + v ] * 3 ;
2019-12-20 11:11:58 +00:00
facet . vertex [ v ] = Vec3f ( geometry . vertices [ tri_id + 0 ] , geometry . vertices [ tri_id + 1 ] , geometry . vertices [ tri_id + 2 ] ) ;
2018-02-27 09:49:51 +00:00
}
}
2019-06-11 15:08:47 +00:00
stl_get_size ( & stl ) ;
triangle_mesh . repair ( ) ;
ModelVolume * volume = object . add_volume ( std : : move ( triangle_mesh ) ) ;
2019-12-20 11:11:58 +00:00
// stores the volume matrix taken from the metadata, if present
if ( has_transform )
volume - > source . transform = Slic3r : : Geometry : : Transformation ( volume_matrix_to_object ) ;
2018-08-15 10:50:06 +00:00
volume - > calculate_convex_hull ( ) ;
2018-02-27 09:49:51 +00:00
2020-07-24 14:53:05 +00:00
// recreate custom supports from previously loaded attribute
for ( unsigned i = 0 ; i < triangles_count ; + + i ) {
2020-08-06 12:05:42 +00:00
size_t index = src_start_id / 3 + i ;
assert ( index < geometry . custom_supports . size ( ) ) ;
if ( ! geometry . custom_supports [ index ] . empty ( ) )
volume - > m_supported_facets . set_triangle_from_string ( i , geometry . custom_supports [ index ] ) ;
2020-07-24 14:53:05 +00:00
}
2019-09-23 10:33:21 +00:00
// apply the remaining volume's metadata
2018-02-27 09:49:51 +00:00
for ( const Metadata & metadata : volume_data . metadata )
{
2018-02-28 11:11:41 +00:00
if ( metadata . key = = NAME_KEY )
2018-02-27 14:46:54 +00:00
volume - > name = metadata . value ;
2018-02-28 11:11:41 +00:00
else if ( ( metadata . key = = MODIFIER_KEY ) & & ( metadata . value = = " 1 " ) )
2019-02-22 11:12:10 +00:00
volume - > set_type ( ModelVolumeType : : PARAMETER_MODIFIER ) ;
2018-09-17 13:12:13 +00:00
else if ( metadata . key = = VOLUME_TYPE_KEY )
volume - > set_type ( ModelVolume : : type_from_string ( metadata . value ) ) ;
2019-09-19 07:09:11 +00:00
else if ( metadata . key = = SOURCE_FILE_KEY )
volume - > source . input_file = metadata . value ;
else if ( metadata . key = = SOURCE_OBJECT_ID_KEY )
volume - > source . object_idx = : : atoi ( metadata . value . c_str ( ) ) ;
else if ( metadata . key = = SOURCE_VOLUME_ID_KEY )
volume - > source . volume_idx = : : atoi ( metadata . value . c_str ( ) ) ;
else if ( metadata . key = = SOURCE_OFFSET_X_KEY )
volume - > source . mesh_offset ( 0 ) = : : atof ( metadata . value . c_str ( ) ) ;
else if ( metadata . key = = SOURCE_OFFSET_Y_KEY )
volume - > source . mesh_offset ( 1 ) = : : atof ( metadata . value . c_str ( ) ) ;
else if ( metadata . key = = SOURCE_OFFSET_Z_KEY )
volume - > source . mesh_offset ( 2 ) = : : atof ( metadata . value . c_str ( ) ) ;
2018-02-27 14:46:54 +00:00
else
volume - > config . set_deserialize ( metadata . key , metadata . value ) ;
2018-02-27 09:49:51 +00:00
}
}
return true ;
}
void XMLCALL _3MF_Importer : : _handle_start_model_xml_element ( void * userData , const char * name , const char * * attributes )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_model_xml_element ( name , attributes ) ;
}
void XMLCALL _3MF_Importer : : _handle_end_model_xml_element ( void * userData , const char * name )
2018-01-30 08:27:10 +00:00
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
2018-02-27 09:49:51 +00:00
importer - > _handle_end_model_xml_element ( name ) ;
2018-01-30 08:27:10 +00:00
}
2018-04-30 13:27:01 +00:00
void XMLCALL _3MF_Importer : : _handle_model_xml_characters ( void * userData , const XML_Char * s , int len )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_model_xml_characters ( s , len ) ;
}
2018-02-27 09:49:51 +00:00
void XMLCALL _3MF_Importer : : _handle_start_config_xml_element ( void * userData , const char * name , const char * * attributes )
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_config_xml_element ( name , attributes ) ;
}
void XMLCALL _3MF_Importer : : _handle_end_config_xml_element ( void * userData , const char * name )
2018-01-30 08:27:10 +00:00
{
_3MF_Importer * importer = ( _3MF_Importer * ) userData ;
if ( importer ! = nullptr )
2018-02-27 09:49:51 +00:00
importer - > _handle_end_config_xml_element ( name ) ;
2018-01-30 08:27:10 +00:00
}
2018-02-14 13:30:03 +00:00
class _3MF_Exporter : public _3MF_Base
2018-02-08 12:26:50 +00:00
{
struct BuildItem
{
unsigned int id ;
2018-08-28 12:30:36 +00:00
Transform3d transform ;
2019-08-06 12:54:38 +00:00
bool printable ;
2018-02-08 12:26:50 +00:00
2019-08-06 12:54:38 +00:00
BuildItem ( unsigned int id , const Transform3d & transform , const bool printable )
2018-02-27 09:49:51 +00:00
: id ( id )
2018-08-28 12:30:36 +00:00
, transform ( transform )
2019-08-06 12:54:38 +00:00
, printable ( printable )
2018-02-27 09:49:51 +00: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 12:26:50 +00:00
} ;
typedef std : : vector < BuildItem > BuildItemsList ;
2018-02-27 09:49:51 +00:00
typedef std : : map < int , ObjectData > IdToObjectDataMap ;
2020-01-08 10:11:38 +00:00
bool m_fullpath_sources { true } ;
2018-02-08 12:26:50 +00:00
public :
2020-01-08 10:11:38 +00:00
bool save_model_to_file ( const std : : string & filename , Model & model , const DynamicPrintConfig * config , bool fullpath_sources , const ThumbnailData * thumbnail_data = nullptr ) ;
2018-02-08 12:26:50 +00:00
private :
2019-10-24 07:20:33 +00:00
bool _save_model_to_file ( const std : : string & filename , Model & model , const DynamicPrintConfig * config , const ThumbnailData * thumbnail_data ) ;
2018-02-13 14:19:55 +00:00
bool _add_content_types_file_to_archive ( mz_zip_archive & archive ) ;
2019-10-24 07:20:33 +00:00
bool _add_thumbnail_file_to_archive ( mz_zip_archive & archive , const ThumbnailData & thumbnail_data ) ;
2018-02-13 14:19:55 +00:00
bool _add_relationships_file_to_archive ( mz_zip_archive & archive ) ;
2020-07-21 07:08:48 +00:00
bool _add_model_file_to_archive ( const std : : string & filename , mz_zip_archive & archive , const Model & model , IdToObjectDataMap & objects_data ) ;
2018-02-27 09:49:51 +00:00
bool _add_object_to_model_stream ( std : : stringstream & stream , unsigned int & object_id , ModelObject & object , BuildItemsList & build_items , VolumeToOffsetsMap & volumes_offsets ) ;
bool _add_mesh_to_object_stream ( std : : stringstream & stream , ModelObject & object , VolumeToOffsetsMap & volumes_offsets ) ;
2018-02-08 12:26:50 +00:00
bool _add_build_to_model_stream ( std : : stringstream & stream , const BuildItemsList & build_items ) ;
2018-04-27 10:56:35 +00:00
bool _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
2019-06-11 12:39:41 +00:00
bool _add_layer_config_ranges_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
2018-10-05 08:13:21 +00:00
bool _add_sla_support_points_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
2019-12-06 14:47:58 +00:00
bool _add_sla_drain_holes_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
2018-11-07 13:57:50 +00:00
bool _add_print_config_file_to_archive ( mz_zip_archive & archive , const DynamicPrintConfig & config ) ;
2019-05-24 10:17:54 +00:00
bool _add_model_config_file_to_archive ( mz_zip_archive & archive , const Model & model , const IdToObjectDataMap & objects_data ) ;
2020-06-08 10:25:29 +00:00
bool _add_custom_gcode_per_print_z_file_to_archive ( mz_zip_archive & archive , Model & model , const DynamicPrintConfig * config ) ;
2018-02-08 12:26:50 +00:00
} ;
2020-01-08 10:11:38 +00:00
bool _3MF_Exporter : : save_model_to_file ( const std : : string & filename , Model & model , const DynamicPrintConfig * config , bool fullpath_sources , const ThumbnailData * thumbnail_data )
{
clear_errors ( ) ;
m_fullpath_sources = fullpath_sources ;
return _save_model_to_file ( filename , model , config , thumbnail_data ) ;
}
2018-02-08 12:26:50 +00:00
2019-10-24 07:20:33 +00:00
bool _3MF_Exporter : : _save_model_to_file ( const std : : string & filename , Model & model , const DynamicPrintConfig * config , const ThumbnailData * thumbnail_data )
2018-02-08 12:26:50 +00:00
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
2019-06-03 13:27:46 +00:00
if ( ! open_zip_writer ( & archive , filename ) ) {
2018-02-14 13:30:03 +00:00
add_error ( " Unable to open the file " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2019-05-24 10:17:54 +00:00
// Adds content types file ("[Content_Types].xml";).
// The content of this file is the same for each PrusaSlicer 3mf.
2018-02-13 14:19:55 +00:00
if ( ! _add_content_types_file_to_archive ( archive ) )
2018-02-08 12:26:50 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-08 12:26:50 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-10-24 07:20:33 +00:00
if ( ( thumbnail_data ! = nullptr ) & & thumbnail_data - > is_valid ( ) )
2019-10-23 11:31:24 +00:00
{
// Adds the file Metadata/thumbnail.png.
2019-10-24 07:20:33 +00:00
if ( ! _add_thumbnail_file_to_archive ( archive , * thumbnail_data ) )
2019-10-23 11:31:24 +00:00
{
close_zip_writer ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
}
2019-05-24 10:17:54 +00:00
// Adds relationships file ("_rels/.rels").
// The content of this file is the same for each PrusaSlicer 3mf.
// The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
2018-02-13 14:19:55 +00:00
if ( ! _add_relationships_file_to_archive ( archive ) )
2018-02-08 12:26:50 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-08 12:26:50 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-05-24 10:17:54 +00:00
// Adds model file ("3D/3dmodel.model").
// This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
IdToObjectDataMap objects_data ;
2020-07-21 07:08:48 +00:00
if ( ! _add_model_file_to_archive ( filename , archive , model , objects_data ) )
2018-02-13 14:19:55 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-13 14:19:55 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-05-24 10:17:54 +00:00
// Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
// All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
2018-04-27 10:56:35 +00:00
if ( ! _add_layer_height_profile_file_to_archive ( archive , model ) )
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-04-27 10:56:35 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-06-11 12:39:41 +00:00
// Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
// All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
if ( ! _add_layer_config_ranges_file_to_archive ( archive , model ) )
{
close_zip_writer ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-05-24 10:17:54 +00:00
// Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
// All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
2018-10-05 08:13:21 +00:00
if ( ! _add_sla_support_points_file_to_archive ( archive , model ) )
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-10-05 08:13:21 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-12-06 14:47:58 +00:00
if ( ! _add_sla_drain_holes_file_to_archive ( archive , model ) )
{
close_zip_writer ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2018-10-05 08:13:21 +00:00
2019-12-17 07:37:50 +00:00
// Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
2019-11-11 15:01:34 +00:00
// All custom gcode per height of whole Model are stored here
2020-06-08 10:25:29 +00:00
if ( ! _add_custom_gcode_per_print_z_file_to_archive ( archive , model , config ) )
2019-11-11 15:01:34 +00:00
{
close_zip_writer ( & archive ) ;
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2019-05-24 10:17:54 +00:00
// Adds slic3r print config file ("Metadata/Slic3r_PE.config").
// This file contains the content of FullPrintConfing / SLAFullPrintConfig.
2018-11-07 13:57:50 +00:00
if ( config ! = nullptr )
2018-02-27 09:49:51 +00:00
{
2018-11-07 13:57:50 +00:00
if ( ! _add_print_config_file_to_archive ( archive , * config ) )
2018-03-22 12:49:48 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-03-22 12:49:48 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
2018-02-27 09:49:51 +00:00
}
2019-05-24 10:17:54 +00:00
// Adds slic3r model config file ("Metadata/Slic3r_PE_model.config").
// This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides).
// As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data
// is stored here as well.
if ( ! _add_model_config_file_to_archive ( archive , model , objects_data ) )
2018-02-08 12:26:50 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-08 12:26:50 +00:00
boost : : filesystem : : remove ( filename ) ;
return false ;
}
if ( ! mz_zip_writer_finalize_archive ( & archive ) )
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-08 12:26:50 +00:00
boost : : filesystem : : remove ( filename ) ;
2018-02-14 13:30:03 +00:00
add_error ( " Unable to finalize the archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-08 12:26:50 +00:00
return true ;
}
2018-02-13 14:19:55 +00:00
bool _3MF_Exporter : : _add_content_types_file_to_archive ( mz_zip_archive & archive )
2018-02-08 12:26:50 +00:00
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <Types xmlns= \" http://schemas.openxmlformats.org/package/2006/content-types \" > \n " ;
stream < < " <Default Extension= \" rels \" ContentType= \" application/vnd.openxmlformats-package.relationships+xml \" /> \n " ;
stream < < " <Default Extension= \" model \" ContentType= \" application/vnd.ms-package.3dmanufacturing-3dmodel+xml \" /> \n " ;
2019-10-23 11:31:24 +00:00
stream < < " <Default Extension= \" png \" ContentType= \" image/png \" /> \n " ;
2018-02-08 12:26:50 +00:00
stream < < " </Types> " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , CONTENT_TYPES_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add content types file to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
return true ;
}
2019-10-24 07:20:33 +00:00
bool _3MF_Exporter : : _add_thumbnail_file_to_archive ( mz_zip_archive & archive , const ThumbnailData & thumbnail_data )
2019-10-23 11:31:24 +00:00
{
2019-10-24 07:20:33 +00:00
bool res = false ;
2019-10-23 11:31:24 +00:00
2019-10-24 07:20:33 +00:00
size_t png_size = 0 ;
void * png_data = tdefl_write_image_to_png_file_in_memory_ex ( ( const void * ) thumbnail_data . pixels . data ( ) , thumbnail_data . width , thumbnail_data . height , 4 , & png_size , MZ_DEFAULT_LEVEL , 1 ) ;
if ( png_data ! = nullptr )
{
res = mz_zip_writer_add_mem ( & archive , THUMBNAIL_FILE . c_str ( ) , ( const void * ) png_data , png_size , MZ_DEFAULT_COMPRESSION ) ;
mz_free ( png_data ) ;
}
if ( ! res )
add_error ( " Unable to add thumbnail file to archive " ) ;
return res ;
2019-10-23 11:31:24 +00:00
}
2018-02-13 14:19:55 +00:00
bool _3MF_Exporter : : _add_relationships_file_to_archive ( mz_zip_archive & archive )
2018-02-08 12:26:50 +00:00
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <Relationships xmlns= \" http://schemas.openxmlformats.org/package/2006/relationships \" > \n " ;
stream < < " <Relationship Target= \" / " < < MODEL_FILE < < " \" Id= \" rel-1 \" Type= \" http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel \" /> \n " ;
2019-10-23 11:31:24 +00:00
stream < < " <Relationship Target= \" / " < < THUMBNAIL_FILE < < " \" Id= \" rel-2 \" Type= \" http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail \" /> \n " ;
2018-02-08 12:26:50 +00:00
stream < < " </Relationships> " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , RELATIONSHIPS_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add relationships file to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
return true ;
}
2020-07-21 07:08:48 +00:00
bool _3MF_Exporter : : _add_model_file_to_archive ( const std : : string & filename , mz_zip_archive & archive , const Model & model , IdToObjectDataMap & objects_data )
2018-02-08 12:26:50 +00:00
{
std : : stringstream stream ;
2019-02-27 09:31:53 +00:00
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
// The default value of std::stream precision is 6 digits only!
2019-02-27 10:08:58 +00:00
stream < < std : : setprecision ( std : : numeric_limits < float > : : max_digits10 ) ;
2018-02-08 12:26:50 +00:00
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
2018-04-30 13:27:01 +00:00
stream < < " < " < < MODEL_TAG < < " unit= \" millimeter \" xml:lang= \" en-US \" xmlns= \" http://schemas.microsoft.com/3dmanufacturing/core/2015/02 \" xmlns:slic3rpe= \" http://schemas.slic3r.org/3mf/2017/06 \" > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" " < < SLIC3RPE_3MF_VERSION < < " \" > " < < VERSION_3MF < < " </ " < < METADATA_TAG < < " > \n " ;
2020-07-21 07:08:48 +00:00
std : : string name = boost : : filesystem : : path ( filename ) . stem ( ) . string ( ) ;
stream < < " < " < < METADATA_TAG < < " name= \" Title \" > " < < name < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" Designer \" > " < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" Description \" > " < < name < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" Copyright \" > " < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" LicenseTerms \" > " < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" Rating \" > " < < " </ " < < METADATA_TAG < < " > \n " ;
std : : string date = Slic3r : : Utils : : utc_timestamp ( Slic3r : : Utils : : get_current_time_utc ( ) ) ;
// keep only the date part of the string
date = date . substr ( 0 , 10 ) ;
stream < < " < " < < METADATA_TAG < < " name= \" CreationDate \" > " < < date < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" ModificationDate \" > " < < date < < " </ " < < METADATA_TAG < < " > \n " ;
stream < < " < " < < METADATA_TAG < < " name= \" Application \" > " < < SLIC3R_APP_KEY < < " - " < < SLIC3R_VERSION < < " </ " < < METADATA_TAG < < " > \n " ;
2018-02-27 09:49:51 +00:00
stream < < " < " < < RESOURCES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
2019-05-24 10:17:54 +00:00
// Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
2018-02-08 12:26:50 +00:00
BuildItemsList build_items ;
2019-05-24 10:17:54 +00:00
// The object_id here is a one based identifier of the first instance of a ModelObject in the 3MF file, where
// all the object instances of all ModelObjects are stored and indexed in a 1 based linear fashion.
// Therefore the list of object_ids here may not be continuous.
2018-02-08 12:26:50 +00:00
unsigned int object_id = 1 ;
for ( ModelObject * obj : model . objects )
{
if ( obj = = nullptr )
continue ;
2019-05-24 10:17:54 +00:00
// Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
2018-02-27 09:49:51 +00:00
unsigned int curr_id = object_id ;
2019-05-24 10:17:54 +00:00
IdToObjectDataMap : : iterator object_it = objects_data . insert ( IdToObjectDataMap : : value_type ( curr_id , ObjectData ( obj ) ) ) . first ;
// Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
// object_it->second.volumes_offsets will contain the offsets of the ModelVolumes in that single indexed triangle set.
// object_id will be increased to point to the 1st instance of the next ModelObject.
2018-02-27 09:49:51 +00:00
if ( ! _add_object_to_model_stream ( stream , object_id , * obj , build_items , object_it - > second . volumes_offsets ) )
2018-02-08 12:26:50 +00:00
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add object to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < RESOURCES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
2019-05-24 10:17:54 +00:00
// Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
2018-02-08 12:26:50 +00:00
if ( ! _add_build_to_model_stream ( stream , build_items ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add build to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < MODEL_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , MODEL_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add model file to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
return true ;
}
2018-02-27 09:49:51 +00:00
bool _3MF_Exporter : : _add_object_to_model_stream ( std : : stringstream & stream , unsigned int & object_id , ModelObject & object , BuildItemsList & build_items , VolumeToOffsetsMap & volumes_offsets )
2018-02-08 12:26:50 +00:00
{
unsigned int id = 0 ;
for ( const ModelInstance * instance : object . instances )
{
2019-05-24 10:17:54 +00:00
assert ( instance ! = nullptr ) ;
2018-02-08 12:26:50 +00:00
if ( instance = = nullptr )
continue ;
unsigned int instance_id = object_id + id ;
2018-02-27 09:49:51 +00:00
stream < < " < " < < OBJECT_TAG < < " id= \" " < < instance_id < < " \" type= \" model \" > \n " ;
2018-02-08 12:26:50 +00:00
if ( id = = 0 )
{
2018-02-27 09:49:51 +00:00
if ( ! _add_mesh_to_object_stream ( stream , object , volumes_offsets ) )
2018-02-08 12:26:50 +00:00
{
2018-02-14 13:30:03 +00:00
add_error ( " Unable to add mesh to archive " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
}
else
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < COMPONENTS_TAG < < " > \n " ;
stream < < " < " < < COMPONENT_TAG < < " objectid= \" " < < object_id < < " \" /> \n " ;
stream < < " </ " < < COMPONENTS_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
}
2018-11-01 13:25:10 +00:00
Transform3d t = instance - > get_matrix ( ) ;
2019-05-24 10:17:54 +00:00
// instance_id is just a 1 indexed index in build_items.
assert ( instance_id = = build_items . size ( ) + 1 ) ;
2019-08-06 12:54:38 +00:00
build_items . emplace_back ( instance_id , t , instance - > printable ) ;
2018-02-08 12:26:50 +00:00
2018-02-27 09:49:51 +00:00
stream < < " </ " < < OBJECT_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
+ + id ;
}
object_id + = id ;
return true ;
}
2018-02-27 09:49:51 +00:00
bool _3MF_Exporter : : _add_mesh_to_object_stream ( std : : stringstream & stream , ModelObject & object , VolumeToOffsetsMap & volumes_offsets )
2018-02-08 12:26:50 +00:00
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < MESH_TAG < < " > \n " ;
stream < < " < " < < VERTICES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
unsigned int vertices_count = 0 ;
for ( ModelVolume * volume : object . volumes )
{
if ( volume = = nullptr )
continue ;
2019-06-11 15:08:47 +00:00
if ( ! volume - > mesh ( ) . repaired )
throw std : : runtime_error ( " store_3mf() requires repair() " ) ;
if ( ! volume - > mesh ( ) . has_shared_vertices ( ) )
throw std : : runtime_error ( " store_3mf() requires shared vertices " ) ;
2018-02-08 12:26:50 +00:00
2019-06-11 15:08:47 +00:00
volumes_offsets . insert ( VolumeToOffsetsMap : : value_type ( volume , Offsets ( vertices_count ) ) ) . first ;
2018-02-08 12:26:50 +00:00
2019-06-11 15:08:47 +00:00
const indexed_triangle_set & its = volume - > mesh ( ) . its ;
2019-06-10 16:30:54 +00:00
if ( its . vertices . empty ( ) )
2018-02-08 12:26:50 +00:00
{
2018-02-14 13:30:03 +00:00
add_error ( " Found invalid mesh " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2019-06-20 14:15:09 +00:00
vertices_count + = ( int ) its . vertices . size ( ) ;
2018-02-08 12:26:50 +00:00
2018-12-14 08:52:15 +00:00
const Transform3d & matrix = volume - > get_matrix ( ) ;
2018-11-19 14:49:59 +00:00
2019-06-10 16:30:54 +00:00
for ( size_t i = 0 ; i < its . vertices . size ( ) ; + + i )
2018-02-08 12:26:50 +00:00
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < VERTEX_TAG < < " " ;
2019-06-10 16:30:54 +00:00
Vec3f v = ( matrix * its . vertices [ i ] . cast < double > ( ) ) . cast < float > ( ) ;
2018-11-19 14:34:34 +00:00
stream < < " x= \" " < < v ( 0 ) < < " \" " ;
stream < < " y= \" " < < v ( 1 ) < < " \" " ;
stream < < " z= \" " < < v ( 2 ) < < " \" /> \n " ;
2018-02-08 12:26:50 +00:00
}
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < VERTICES_TAG < < " > \n " ;
stream < < " < " < < TRIANGLES_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
2018-02-27 09:49:51 +00:00
unsigned int triangles_count = 0 ;
2018-02-08 12:26:50 +00:00
for ( ModelVolume * volume : object . volumes )
{
if ( volume = = nullptr )
continue ;
2018-02-27 09:49:51 +00:00
VolumeToOffsetsMap : : iterator volume_it = volumes_offsets . find ( volume ) ;
assert ( volume_it ! = volumes_offsets . end ( ) ) ;
2018-02-08 12:26:50 +00:00
2019-06-11 15:08:47 +00:00
const indexed_triangle_set & its = volume - > mesh ( ) . its ;
2018-02-08 12:26:50 +00:00
2018-02-27 09:49:51 +00:00
// updates triangle offsets
volume_it - > second . first_triangle_id = triangles_count ;
2019-06-20 14:15:09 +00:00
triangles_count + = ( int ) its . indices . size ( ) ;
2018-02-27 09:49:51 +00:00
volume_it - > second . last_triangle_id = triangles_count - 1 ;
2019-06-10 16:30:54 +00:00
for ( size_t i = 0 ; i < its . indices . size ( ) ; + + i )
2018-02-08 12:26:50 +00:00
{
2018-02-27 09:49:51 +00:00
stream < < " < " < < TRIANGLE_TAG < < " " ;
2018-02-08 12:26:50 +00:00
for ( int j = 0 ; j < 3 ; + + j )
{
2019-06-10 19:14:58 +00:00
stream < < " v " < < j + 1 < < " = \" " < < its . indices [ i ] [ j ] + volume_it - > second . first_vertex_id < < " \" " ;
2018-02-08 12:26:50 +00:00
}
2020-07-24 14:53:05 +00:00
std : : string custom_supports_data_string = volume - > m_supported_facets . get_triangle_as_string ( i ) ;
if ( ! custom_supports_data_string . empty ( ) )
stream < < CUSTOM_SUPPORTS_ATTR < < " = \" " < < custom_supports_data_string < < " \" " ;
2018-02-08 12:26:50 +00:00
stream < < " /> \n " ;
}
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < TRIANGLES_TAG < < " > \n " ;
stream < < " </ " < < MESH_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
return true ;
}
bool _3MF_Exporter : : _add_build_to_model_stream ( std : : stringstream & stream , const BuildItemsList & build_items )
{
if ( build_items . size ( ) = = 0 )
{
2018-02-14 13:30:03 +00:00
add_error ( " No build item found " ) ;
2018-02-08 12:26:50 +00:00
return false ;
}
2018-02-27 09:49:51 +00:00
stream < < " < " < < BUILD_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
for ( const BuildItem & item : build_items )
{
2019-09-19 07:09:11 +00:00
stream < < " < " < < ITEM_TAG < < " " < < OBJECTID_ATTR < < " = \" " < < item . id < < " \" " < < TRANSFORM_ATTR < < " = \" " ;
2018-02-08 12:26:50 +00:00
for ( unsigned c = 0 ; c < 4 ; + + c )
{
for ( unsigned r = 0 ; r < 3 ; + + r )
{
2018-08-28 12:30:36 +00:00
stream < < item . transform ( r , c ) ;
2018-02-08 12:26:50 +00:00
if ( ( r ! = 2 ) | | ( c ! = 3 ) )
stream < < " " ;
}
}
2019-09-19 07:09:11 +00:00
stream < < " \" " < < PRINTABLE_ATTR < < " = \" " < < item . printable < < " \" /> \n " ;
2018-02-08 12:26:50 +00:00
}
2018-02-27 09:49:51 +00:00
stream < < " </ " < < BUILD_TAG < < " > \n " ;
2018-02-08 12:26:50 +00:00
return true ;
}
2018-04-27 10:56:35 +00:00
bool _3MF_Exporter : : _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model )
{
std : : string out = " " ;
char buffer [ 1024 ] ;
unsigned int count = 0 ;
for ( const ModelObject * object : model . objects )
{
+ + count ;
2019-01-21 09:06:51 +00:00
const std : : vector < double > & layer_height_profile = object - > layer_height_profile ;
2018-04-27 10:56:35 +00:00
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 ;
}
2019-06-11 12:39:41 +00:00
bool _3MF_Exporter : : _add_layer_config_ranges_file_to_archive ( mz_zip_archive & archive , Model & model )
{
std : : string out = " " ;
2019-06-27 11:54:51 +00:00
pt : : ptree tree ;
2019-06-11 12:39:41 +00:00
unsigned int object_cnt = 0 ;
for ( const ModelObject * object : model . objects )
{
object_cnt + + ;
const t_layer_config_ranges & ranges = object - > layer_config_ranges ;
if ( ! ranges . empty ( ) )
{
2019-06-27 11:54:51 +00:00
pt : : ptree & obj_tree = tree . add ( " objects.object " , " " ) ;
obj_tree . put ( " <xmlattr>.id " , object_cnt ) ;
2019-06-11 12:39:41 +00:00
// Store the layer config ranges.
for ( const auto & range : ranges )
{
2019-06-27 13:57:29 +00:00
pt : : ptree & range_tree = obj_tree . add ( " range " , " " ) ;
2019-06-27 11:54:51 +00:00
2019-06-11 12:39:41 +00:00
// store minX and maxZ
2019-06-27 11:54:51 +00:00
range_tree . put ( " <xmlattr>.min_z " , range . first . first ) ;
range_tree . put ( " <xmlattr>.max_z " , range . first . second ) ;
2019-06-11 12:39:41 +00:00
// store range configuration
const DynamicPrintConfig & config = range . second ;
2019-06-27 11:54:51 +00:00
for ( const std : : string & opt_key : config . keys ( ) )
{
2019-07-04 18:49:46 +00:00
pt : : ptree & opt_tree = range_tree . add ( " option " , config . opt_serialize ( opt_key ) ) ;
2019-06-27 11:54:51 +00:00
opt_tree . put ( " <xmlattr>.opt_key " , opt_key ) ;
}
2019-06-11 12:39:41 +00:00
}
}
}
2019-06-27 11:54:51 +00:00
if ( ! tree . empty ( ) )
{
std : : ostringstream oss ;
2019-11-11 15:01:34 +00:00
pt : : write_xml ( oss , tree ) ;
2019-06-27 11:54:51 +00:00
out = oss . str ( ) ;
2019-06-27 13:57:29 +00:00
// Post processing("beautification") of the output string for a better preview
2019-06-27 11:54:51 +00:00
boost : : replace_all ( out , " ><object " , " > \n <object " ) ;
2019-06-27 13:57:29 +00:00
boost : : replace_all ( out , " ><range " , " > \n <range " ) ;
boost : : replace_all ( out , " ><option " , " > \n <option " ) ;
boost : : replace_all ( out , " ></range> " , " > \n </range> " ) ;
boost : : replace_all ( out , " ></object> " , " > \n </object> " ) ;
// OR just
boost : : replace_all ( out , " >< " , " > \n < " ) ;
2019-06-27 11:54:51 +00:00
}
2019-06-11 12:39:41 +00:00
if ( ! out . empty ( ) )
{
if ( ! mz_zip_writer_add_mem ( & archive , LAYER_CONFIG_RANGES_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 08:13:21 +00: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 ;
2019-01-30 07:26:23 +00:00
const std : : vector < sla : : SupportPoint > & sla_support_points = object - > sla_support_points ;
2018-10-05 08:13:21 +00:00
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 )
{
2019-01-30 07:26:23 +00:00
sprintf ( buffer , ( i = = 0 ? " %f %f %f %f %f " : " %f %f %f %f %f " ) , sla_support_points [ i ] . pos ( 0 ) , sla_support_points [ i ] . pos ( 1 ) , sla_support_points [ i ] . pos ( 2 ) , sla_support_points [ i ] . head_front_radius , ( float ) sla_support_points [ i ] . is_new_island ) ;
2018-10-05 08:13:21 +00:00
out + = buffer ;
}
out + = " \n " ;
}
}
if ( ! out . empty ( ) )
{
2019-02-01 15:12:00 +00:00
// Adds version header at the beginning:
out = std : : string ( " support_points_format_version= " ) + std : : to_string ( support_points_format_version ) + std : : string ( " \n " ) + out ;
2018-10-05 08:13:21 +00:00
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 ;
}
2019-12-06 14:47:58 +00:00
bool _3MF_Exporter : : _add_sla_drain_holes_file_to_archive ( mz_zip_archive & archive , Model & model )
{
const char * const fmt = " object_id=%d| " ;
std : : string out ;
unsigned int count = 0 ;
for ( const ModelObject * object : model . objects )
{
+ + count ;
auto & drain_holes = object - > sla_drain_holes ;
if ( ! drain_holes . empty ( ) )
{
out + = string_printf ( fmt , count ) ;
// Store the layer height profile as a single space separated list.
for ( size_t i = 0 ; i < drain_holes . size ( ) ; + + i )
out + = string_printf ( ( i = = 0 ? " %f %f %f %f %f %f %f %f " : " %f %f %f %f %f %f %f %f " ) ,
drain_holes [ i ] . pos ( 0 ) ,
drain_holes [ i ] . pos ( 1 ) ,
drain_holes [ i ] . pos ( 2 ) ,
drain_holes [ i ] . normal ( 0 ) ,
drain_holes [ i ] . normal ( 1 ) ,
drain_holes [ i ] . normal ( 2 ) ,
drain_holes [ i ] . radius ,
drain_holes [ i ] . height ) ;
out + = " \n " ;
}
}
if ( ! out . empty ( ) )
{
// Adds version header at the beginning:
out = std : : string ( " drain_holes_format_version= " ) + std : : to_string ( drain_holes_format_version ) + std : : string ( " \n " ) + out ;
if ( ! mz_zip_writer_add_mem ( & archive , SLA_DRAIN_HOLES_FILE . c_str ( ) , static_cast < const void * > ( out . data ( ) ) , out . length ( ) , mz_uint ( MZ_DEFAULT_COMPRESSION ) ) )
{
add_error ( " Unable to add sla support points file to archive " ) ;
return false ;
}
}
return true ;
}
2018-10-05 08:13:21 +00:00
2018-11-07 13:57:50 +00:00
bool _3MF_Exporter : : _add_print_config_file_to_archive ( mz_zip_archive & archive , const DynamicPrintConfig & config )
2018-02-13 14:19:55 +00:00
{
char buffer [ 1024 ] ;
sprintf ( buffer , " ; %s \n \n " , header_slic3r_generated ( ) . c_str ( ) ) ;
std : : string out = buffer ;
2018-11-07 13:57:50 +00:00
for ( const std : : string & key : config . keys ( ) )
if ( key ! = " compatible_printers " )
2019-06-26 11:26:49 +00:00
out + = " ; " + key + " = " + config . opt_serialize ( key ) + " \n " ;
2018-02-13 14:19:55 +00:00
2018-04-27 10:56:35 +00:00
if ( ! out . empty ( ) )
2018-02-13 14:19:55 +00:00
{
2018-04-27 10:56:35 +00:00
if ( ! mz_zip_writer_add_mem ( & archive , PRINT_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
add_error ( " Unable to add print config file to archive " ) ;
return false ;
}
2018-02-27 09:49:51 +00:00
}
return true ;
}
2019-05-24 10:17:54 +00:00
bool _3MF_Exporter : : _add_model_config_file_to_archive ( mz_zip_archive & archive , const Model & model , const IdToObjectDataMap & objects_data )
2018-02-27 09:49:51 +00:00
{
std : : stringstream stream ;
2019-12-19 09:22:46 +00:00
// Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
// when loaded as accurately as possible.
stream < < std : : setprecision ( std : : numeric_limits < double > : : max_digits10 ) ;
2018-02-27 09:49:51 +00:00
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " < " < < CONFIG_TAG < < " > \n " ;
2019-05-24 10:17:54 +00:00
for ( const IdToObjectDataMap : : value_type & obj_metadata : objects_data )
2018-02-27 09:49:51 +00:00
{
const ModelObject * obj = obj_metadata . second . object ;
if ( obj ! = nullptr )
{
2020-01-02 10:37:50 +00:00
// Output of instances count added because of github #3435, currently not used by PrusaSlicer
stream < < " < " < < OBJECT_TAG < < " " < < ID_ATTR < < " = \" " < < obj_metadata . first < < " \" " < < INSTANCESCOUNT_ATTR < < " = \" " < < obj - > instances . size ( ) < < " \" > \n " ;
2018-02-27 09:49:51 +00:00
2018-02-27 14:46:54 +00:00
// stores object's name
if ( ! obj - > name . empty ( ) )
2018-07-23 12:39:50 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < OBJECT_TYPE < < " \" " < < KEY_ATTR < < " = \" name \" " < < VALUE_ATTR < < " = \" " < < xml_escape ( obj - > name ) < < " \" /> \n " ;
2018-02-27 14:46:54 +00:00
2018-02-27 09:49:51 +00:00
// stores object's config data
for ( const std : : string & key : obj - > config . keys ( ) )
{
2019-06-26 11:26:49 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < OBJECT_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < key < < " \" " < < VALUE_ATTR < < " = \" " < < obj - > config . opt_serialize ( key ) < < " \" /> \n " ;
2018-02-27 09:49:51 +00:00
}
for ( const ModelVolume * volume : obj_metadata . second . object - > volumes )
{
if ( volume ! = nullptr )
{
const VolumeToOffsetsMap & offsets = obj_metadata . second . volumes_offsets ;
VolumeToOffsetsMap : : const_iterator it = offsets . find ( volume ) ;
if ( it ! = offsets . end ( ) )
{
// stores volume's offsets
stream < < " < " < < VOLUME_TAG < < " " ;
stream < < FIRST_TRIANGLE_ID_ATTR < < " = \" " < < it - > second . first_triangle_id < < " \" " ;
stream < < LAST_TRIANGLE_ID_ATTR < < " = \" " < < it - > second . last_triangle_id < < " \" > \n " ;
2018-02-27 14:46:54 +00:00
// stores volume's name
if ( ! volume - > name . empty ( ) )
2018-07-23 12:39:50 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < NAME_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < xml_escape ( volume - > name ) < < " \" /> \n " ;
2018-02-28 11:11:41 +00:00
2018-09-17 13:12:13 +00:00
// stores volume's modifier field (legacy, to support old slicers)
if ( volume - > is_modifier ( ) )
2018-02-28 11:11:41 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < MODIFIER_KEY < < " \" " < < VALUE_ATTR < < " = \" 1 \" /> \n " ;
2018-09-17 13:12:13 +00: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 14:46:54 +00:00
2019-09-19 07:09:11 +00:00
// stores volume's local matrix
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < MATRIX_KEY < < " \" " < < VALUE_ATTR < < " = \" " ;
2019-12-20 11:11:58 +00:00
Transform3d matrix = volume - > get_matrix ( ) * volume - > source . transform . get_matrix ( ) ;
2019-09-19 07:09:11 +00:00
for ( int r = 0 ; r < 4 ; + + r )
{
for ( int c = 0 ; c < 4 ; + + c )
{
stream < < matrix ( r , c ) ;
if ( ( r ! = 3 ) | | ( c ! = 3 ) )
stream < < " " ;
}
}
stream < < " \" /> \n " ;
// stores volume's source data
if ( ! volume - > source . input_file . empty ( ) )
{
2020-01-08 10:11:38 +00:00
std : : string input_file = xml_escape ( m_fullpath_sources ? volume - > source . input_file : boost : : filesystem : : path ( volume - > source . input_file ) . filename ( ) . string ( ) ) ;
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < SOURCE_FILE_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < input_file < < " \" /> \n " ;
2019-09-19 07:09:11 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < SOURCE_OBJECT_ID_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . object_idx < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < SOURCE_VOLUME_ID_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . volume_idx < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < SOURCE_OFFSET_X_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . mesh_offset ( 0 ) < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < SOURCE_OFFSET_Y_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . mesh_offset ( 1 ) < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < SOURCE_OFFSET_Z_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . mesh_offset ( 2 ) < < " \" /> \n " ;
}
2018-02-27 09:49:51 +00:00
// stores volume's config data
for ( const std : : string & key : volume - > config . keys ( ) )
{
2019-06-26 11:26:49 +00:00
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < VOLUME_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < key < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > config . opt_serialize ( key ) < < " \" /> \n " ;
2018-02-27 09:49:51 +00:00
}
2018-02-27 14:46:54 +00:00
2018-02-27 09:49:51 +00:00
stream < < " </ " < < VOLUME_TAG < < " > \n " ;
}
}
}
stream < < " </ " < < OBJECT_TAG < < " > \n " ;
}
}
stream < < " </ " < < CONFIG_TAG < < " > \n " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , MODEL_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
{
add_error ( " Unable to add model config file to archive " ) ;
2018-02-13 14:19:55 +00:00
return false ;
}
return true ;
}
2020-06-08 10:25:29 +00:00
bool _3MF_Exporter : : _add_custom_gcode_per_print_z_file_to_archive ( mz_zip_archive & archive , Model & model , const DynamicPrintConfig * config )
2019-11-11 15:01:34 +00:00
{
std : : string out = " " ;
2020-01-16 15:01:19 +00:00
if ( ! model . custom_gcode_per_print_z . gcodes . empty ( ) )
2019-11-11 15:01:34 +00:00
{
pt : : ptree tree ;
2019-12-17 07:37:50 +00:00
pt : : ptree & main_tree = tree . add ( " custom_gcodes_per_print_z " , " " ) ;
2019-11-11 15:01:34 +00:00
2020-01-23 11:49:39 +00:00
for ( const CustomGCode : : Item & code : model . custom_gcode_per_print_z . gcodes )
2019-11-11 15:01:34 +00:00
{
pt : : ptree & code_tree = main_tree . add ( " code " , " " ) ;
2020-06-08 10:25:29 +00:00
// store data of custom_gcode_per_print_z
2019-12-17 07:37:50 +00:00
code_tree . put ( " <xmlattr>.print_z " , code . print_z ) ;
2020-06-08 10:25:29 +00:00
code_tree . put ( " <xmlattr>.type " , static_cast < int > ( code . type ) ) ;
2019-11-11 15:01:34 +00:00
code_tree . put ( " <xmlattr>.extruder " , code . extruder ) ;
code_tree . put ( " <xmlattr>.color " , code . color ) ;
2020-06-08 10:25:29 +00:00
code_tree . put ( " <xmlattr>.extra " , code . extra ) ;
// add gcode field data for the old version of the PrusaSlicer
std : : string gcode = code . type = = CustomGCode : : ColorChange ? config - > opt_string ( " color_change_gcode " ) :
code . type = = CustomGCode : : PausePrint ? config - > opt_string ( " pause_print_gcode " ) :
code . type = = CustomGCode : : Template ? config - > opt_string ( " template_custom_gcode " ) :
code . type = = CustomGCode : : ToolChange ? " tool_change " : code . extra ;
code_tree . put ( " <xmlattr>.gcode " , gcode ) ;
2020-01-23 11:49:39 +00:00
}
pt : : ptree & mode_tree = main_tree . add ( " mode " , " " ) ;
// store mode of a custom_gcode_per_print_z
mode_tree . put ( " <xmlattr>.value " , model . custom_gcode_per_print_z . mode = = CustomGCode : : Mode : : SingleExtruder ? CustomGCode : : SingleExtruderMode :
model . custom_gcode_per_print_z . mode = = CustomGCode : : Mode : : MultiAsSingle ? CustomGCode : : MultiAsSingleMode :
CustomGCode : : MultiExtruderMode ) ;
2019-11-11 15:01:34 +00:00
if ( ! tree . empty ( ) )
{
std : : ostringstream oss ;
boost : : property_tree : : write_xml ( oss , tree ) ;
out = oss . str ( ) ;
// Post processing("beautification") of the output string
boost : : replace_all ( out , " >< " , " > \n < " ) ;
}
}
if ( ! out . empty ( ) )
{
2019-12-17 07:37:50 +00:00
if ( ! mz_zip_writer_add_mem ( & archive , CUSTOM_GCODE_PER_PRINT_Z_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
2019-11-11 15:01:34 +00:00
{
2019-12-17 07:37:50 +00:00
add_error ( " Unable to add custom Gcodes per print_z file to archive " ) ;
2019-11-11 15:01:34 +00:00
return false ;
}
}
return true ;
}
bool load_3mf ( const char * path , DynamicPrintConfig * config , Model * model , bool check_version )
2018-01-30 08:27:10 +00:00
{
2018-09-25 09:53:05 +00:00
if ( ( path = = nullptr ) | | ( config = = nullptr ) | | ( model = = nullptr ) )
2018-01-30 08:27:10 +00:00
return false ;
_3MF_Importer importer ;
2019-08-23 11:12:31 +00:00
bool res = importer . load_model_from_file ( path , * model , * config , check_version ) ;
2018-04-27 10:56:35 +00:00
importer . log_errors ( ) ;
2018-01-30 08:27:10 +00:00
return res ;
}
2018-02-08 12:26:50 +00:00
2020-01-08 10:11:38 +00:00
bool store_3mf ( const char * path , Model * model , const DynamicPrintConfig * config , bool fullpath_sources , const ThumbnailData * thumbnail_data )
2018-02-08 12:26:50 +00:00
{
2018-11-07 13:57:50 +00:00
if ( ( path = = nullptr ) | | ( model = = nullptr ) )
2018-02-08 12:26:50 +00:00
return false ;
_3MF_Exporter exporter ;
2020-01-08 10:11:38 +00:00
bool res = exporter . save_model_to_file ( path , * model , config , fullpath_sources , thumbnail_data ) ;
2018-02-08 12:26:50 +00:00
if ( ! res )
2018-02-14 13:30:03 +00:00
exporter . log_errors ( ) ;
2018-02-08 12:26:50 +00:00
return res ;
}
2018-01-30 08:27:10 +00:00
} // namespace Slic3r