2024-09-03 09:34:33 +08:00
# include "../libslic3r.h"
# include "../Exception.hpp"
# include "../Model.hpp"
# include "../Preset.hpp"
# include "../Utils.hpp"
# include "../LocalesUtils.hpp"
# include "../GCode.hpp"
# include "../Geometry.hpp"
# include "../GCode/ThumbnailData.hpp"
# include "../Semver.hpp"
# include "../Time.hpp"
# include "../I18N.hpp"
# include "qds_3mf.hpp"
# include <limits>
# include <stdexcept>
# include <iomanip>
# include <boost/algorithm/string/classification.hpp>
# include <boost/algorithm/string/split.hpp>
# include <boost/algorithm/string/predicate.hpp>
# include <boost/algorithm/string/replace.hpp>
# include <boost/filesystem/operations.hpp>
# include <boost/filesystem/string_file.hpp>
# include <boost/lexical_cast.hpp>
# include <boost/nowide/fstream.hpp>
# include <boost/nowide/cstdio.hpp>
# include <boost/spirit/include/karma.hpp>
# include <boost/spirit/include/qi_int.hpp>
# include <boost/log/trivial.hpp>
# include <boost/beast/core/detail/base64.hpp>
# include <boost/property_tree/ptree.hpp>
# include <boost/property_tree/xml_parser.hpp>
# include <boost/foreach.hpp>
# include <openssl/md5.h>
namespace pt = boost : : property_tree ;
# include <tbb/blocked_range.h>
# include <tbb/parallel_for.h>
# include <tbb/parallel_reduce.h>
# include <expat.h>
# include <Eigen/Dense>
# include "miniz_extension.hpp"
# include "nlohmann/json.hpp"
# include <fast_float/fast_float.h>
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
// https://github.com/boostorg/spirit/pull/586
// where the exported string is one digit shorter than it should be to guarantee lossless round trip.
// The code is left here for the ocasion boost guys improve.
# define EXPORT_3MF_USE_SPIRIT_KARMA_FP 0
# define WRITE_ZIP_LANGUAGE_ENCODING 1
// @see https://commons.apache.org/proper/commons-compress/apidocs/src-html/org/apache/commons/compress/archivers/zip/AbstractUnicodeExtraField.html
struct ZipUnicodePathExtraField
{
static std : : string encode ( std : : string const & u8path , std : : string const & path ) {
std : : string extra ;
if ( u8path ! = path ) {
// 0x7075 - for Unicode filenames
extra . push_back ( ' \x75 ' ) ;
extra . push_back ( ' \x70 ' ) ;
boost : : uint16_t len = 5 + u8path . length ( ) ;
extra . push_back ( ( char ) ( len & 0xff ) ) ;
extra . push_back ( ( char ) ( len > > 8 ) ) ;
auto crc = mz_crc32 ( 0 , ( unsigned char * ) path . c_str ( ) , path . length ( ) ) ;
extra . push_back ( ' \x01 ' ) ; // version 1
extra . append ( ( char * ) & crc , ( char * ) & crc + 4 ) ; // Little Endian
extra . append ( u8path ) ;
}
return extra ;
}
static std : : string decode ( std : : string const & extra , std : : string const & path = { } ) {
char const * p = extra . data ( ) ;
char const * e = p + extra . length ( ) ;
while ( p + 4 < e ) {
boost : : uint16_t len = ( ( boost : : uint16_t ) p [ 2 ] ) | ( ( boost : : uint16_t ) p [ 3 ] < < 8 ) ;
if ( p [ 0 ] = = ' \x75 ' & & p [ 1 ] = = ' \x70 ' & & len > = 5 & & p + 4 + len < e & & p [ 4 ] = = ' \x01 ' ) {
return std : : string ( p + 9 , p + 4 + len ) ;
}
else {
p + = 4 + len ;
}
}
return Slic3r : : decode_path ( path . c_str ( ) ) ;
}
} ;
// 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.
// 2 : Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file, meshes transformed back to their coordinate system on loading.
// WARNING !! -> the version number has been rolled back to 1
// the next change should use 3
const unsigned int VERSION_QDS_3MF = 1 ;
// Allow loading version 2 file as well.
const unsigned int VERSION_QDS_3MF_COMPATIBLE = 2 ;
const char * QDS_3MF_VERSION1 = " qidi_slicer:Version3mf " ; // definition of the metadata name saved into .model file
const char * QDS_3MF_VERSION = " QIDIStudio:3mfVersion " ; //compatible with prusa currently
// Painting gizmos data version numbers
// 0 : initial version of fdm, seam, mm
const unsigned int FDM_SUPPORTS_PAINTING_VERSION = 0 ;
const unsigned int SEAM_PAINTING_VERSION = 0 ;
const unsigned int MM_PAINTING_VERSION = 0 ;
const std : : string QDS_FDM_SUPPORTS_PAINTING_VERSION = " QIDIStudio:FdmSupportsPaintingVersion " ;
const std : : string QDS_SEAM_PAINTING_VERSION = " QIDIStudio:SeamPaintingVersion " ;
const std : : string QDS_MM_PAINTING_VERSION = " QIDIStudio:MmPaintingVersion " ;
const std : : string QDT_MODEL_ID_TAG = " model_id " ;
const std : : string QDT_MODEL_NAME_TAG = " Title " ;
const std : : string QDT_ORIGIN_TAG = " Origin " ;
const std : : string QDT_DESIGNER_TAG = " Designer " ;
const std : : string QDT_DESIGNER_USER_ID_TAG = " DesignerUserId " ;
const std : : string QDT_DESIGN_ID_TAG = " DesignId " ;
//const std::string QDT_DESIGNER_MODEL_ID_TAG = "DesignModelId";
const std : : string QDT_DESIGNER_COVER_FILE_TAG = " DesignerCover " ;
const std : : string QDT_DESCRIPTION_TAG = " Description " ;
const std : : string QDT_COPYRIGHT_TAG = " CopyRight " ;
const std : : string QDT_COPYRIGHT_NORMATIVE_TAG = " Copyright " ;
const std : : string QDT_LICENSE_TAG = " License " ;
const std : : string QDT_REGION_TAG = " Region " ;
const std : : string QDT_MODIFICATION_TAG = " ModificationDate " ;
const std : : string QDT_CREATION_DATE_TAG = " CreationDate " ;
const std : : string QDT_APPLICATION_TAG = " Application " ;
const std : : string QDT_MAKERLAB_TAG = " MakerLab " ;
const std : : string QDT_MAKERLAB_VERSION_TAG = " MakerLabVersion " ;
const std : : string QDT_PROFILE_TITLE_TAG = " ProfileTitle " ;
const std : : string QDT_PROFILE_COVER_TAG = " ProfileCover " ;
const std : : string QDT_PROFILE_DESCRIPTION_TAG = " ProfileDescription " ;
const std : : string QDT_PROFILE_USER_ID_TAG = " ProfileUserId " ;
const std : : string QDT_PROFILE_USER_NAME_TAG = " ProfileUserName " ;
const std : : string MODEL_FOLDER = " 3D/ " ;
const std : : string MODEL_EXTENSION = " .model " ;
const std : : string MODEL_FILE = " 3D/3dmodel.model " ; // << this is the only format of the string which works with CURA
const std : : string MODEL_RELS_FILE = " 3D/_rels/3dmodel.model.rels " ;
//QDS: add metadata_folder
const std : : string METADATA_DIR = " Metadata/ " ;
const std : : string ACCESOR_DIR = " accesories/ " ;
const std : : string GCODE_EXTENSION = " .gcode " ;
const std : : string THUMBNAIL_EXTENSION = " .png " ;
const std : : string CALIBRATION_INFO_EXTENSION = " .json " ;
const std : : string CONTENT_TYPES_FILE = " [Content_Types].xml " ;
const std : : string RELATIONSHIPS_FILE = " _rels/.rels " ;
const std : : string THUMBNAIL_FILE = " Metadata/plate_1.png " ;
const std : : string THUMBNAIL_FOR_PRINTER_FILE = " Metadata/qdt_thumbnail.png " ;
const std : : string PRINTER_THUMBNAIL_SMALL_FILE = " /Auxiliaries/.thumbnails/thumbnail_small.png " ;
const std : : string PRINTER_THUMBNAIL_MIDDLE_FILE = " /Auxiliaries/.thumbnails/thumbnail_middle.png " ;
const std : : string _3MF_COVER_FILE = " /Auxiliaries/.thumbnails/thumbnail_3mf.png " ;
//const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
//const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std : : string QDS_PRINT_CONFIG_FILE = " Metadata/print_profile.config " ;
const std : : string QDS_PROJECT_CONFIG_FILE = " Metadata/project_settings.config " ;
const std : : string QDS_MODEL_CONFIG_FILE = " Metadata/model_settings.config " ;
const std : : string QDS_MODEL_CONFIG_RELS_FILE = " Metadata/_rels/model_settings.config.rels " ;
const std : : string SLICE_INFO_CONFIG_FILE = " Metadata/slice_info.config " ;
const std : : string QDS_LAYER_HEIGHTS_PROFILE_FILE = " Metadata/layer_heights_profile.txt " ;
const std : : string LAYER_CONFIG_RANGES_FILE = " Metadata/layer_config_ranges.xml " ;
/*const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
const std : : string SLA_DRAIN_HOLES_FILE = " Metadata/Slic3r_PE_sla_drain_holes.txt " ; */
const std : : string CUSTOM_GCODE_PER_PRINT_Z_FILE = " Metadata/custom_gcode_per_layer.xml " ;
const std : : string AUXILIARY_DIR = " Auxiliaries/ " ;
const std : : string PROJECT_EMBEDDED_PRINT_PRESETS_FILE = " Metadata/print_setting_ " ;
const std : : string PROJECT_EMBEDDED_SLICE_PRESETS_FILE = " Metadata/process_settings_ " ;
const std : : string PROJECT_EMBEDDED_FILAMENT_PRESETS_FILE = " Metadata/filament_settings_ " ;
const std : : string PROJECT_EMBEDDED_PRINTER_PRESETS_FILE = " Metadata/machine_settings_ " ;
const std : : string CUT_INFORMATION_FILE = " Metadata/cut_information.xml " ;
const unsigned int AUXILIARY_STR_LEN = 12 ;
const unsigned int METADATA_STR_LEN = 9 ;
static constexpr const char * MODEL_TAG = " model " ;
static constexpr const char * RESOURCES_TAG = " resources " ;
static constexpr const char * COLOR_GROUP_TAG = " m:colorgroup " ;
static constexpr const char * COLOR_TAG = " m:color " ;
static constexpr const char * OBJECT_TAG = " object " ;
static constexpr const char * MESH_TAG = " mesh " ;
static constexpr const char * MESH_STAT_TAG = " mesh_stat " ;
static constexpr const char * VERTICES_TAG = " vertices " ;
static constexpr const char * VERTEX_TAG = " vertex " ;
static constexpr const char * TRIANGLES_TAG = " triangles " ;
static constexpr const char * TRIANGLE_TAG = " triangle " ;
static constexpr const char * COMPONENTS_TAG = " components " ;
static constexpr const char * COMPONENT_TAG = " component " ;
static constexpr const char * BUILD_TAG = " build " ;
static constexpr const char * ITEM_TAG = " item " ;
static constexpr const char * METADATA_TAG = " metadata " ;
static constexpr const char * FILAMENT_TAG = " filament " ;
static constexpr const char * SLICE_WARNING_TAG = " warning " ;
static constexpr const char * WARNING_MSG_TAG = " msg " ;
static constexpr const char * FILAMENT_ID_TAG = " id " ;
static constexpr const char * FILAMENT_TYPE_TAG = " type " ;
static constexpr const char * FILAMENT_COLOR_TAG = " color " ;
static constexpr const char * FILAMENT_USED_M_TAG = " used_m " ;
static constexpr const char * FILAMENT_USED_G_TAG = " used_g " ;
static constexpr const char * FILAMENT_TRAY_INFO_ID_TAG = " tray_info_idx " ;
static constexpr const char * CONFIG_TAG = " config " ;
static constexpr const char * VOLUME_TAG = " volume " ;
static constexpr const char * PART_TAG = " part " ;
static constexpr const char * PLATE_TAG = " plate " ;
static constexpr const char * INSTANCE_TAG = " model_instance " ;
//QDS
static constexpr const char * ASSEMBLE_TAG = " assemble " ;
static constexpr const char * ASSEMBLE_ITEM_TAG = " assemble_item " ;
static constexpr const char * SLICE_HEADER_TAG = " header " ;
static constexpr const char * SLICE_HEADER_ITEM_TAG = " header_item " ;
// text_info
static constexpr const char * TEXT_INFO_TAG = " text_info " ;
static constexpr const char * TEXT_ATTR = " text " ;
static constexpr const char * FONT_NAME_ATTR = " font_name " ;
static constexpr const char * FONT_INDEX_ATTR = " font_index " ;
static constexpr const char * FONT_SIZE_ATTR = " font_size " ;
static constexpr const char * THICKNESS_ATTR = " thickness " ;
static constexpr const char * EMBEDED_DEPTH_ATTR = " embeded_depth " ;
static constexpr const char * ROTATE_ANGLE_ATTR = " rotate_angle " ;
static constexpr const char * TEXT_GAP_ATTR = " text_gap " ;
static constexpr const char * BOLD_ATTR = " bold " ;
static constexpr const char * ITALIC_ATTR = " italic " ;
static constexpr const char * SURFACE_TEXT_ATTR = " surface_text " ;
static constexpr const char * KEEP_HORIZONTAL_ATTR = " keep_horizontal " ;
static constexpr const char * HIT_MESH_ATTR = " hit_mesh " ;
static constexpr const char * HIT_POSITION_ATTR = " hit_position " ;
static constexpr const char * HIT_NORMAL_ATTR = " hit_normal " ;
// QDS: encrypt
static constexpr const char * RELATIONSHIP_TAG = " Relationship " ;
static constexpr const char * PID_ATTR = " pid " ;
static constexpr const char * PUUID_ATTR = " p:UUID " ;
static constexpr const char * PUUID_LOWER_ATTR = " p:uuid " ;
static constexpr const char * PPATH_ATTR = " p:path " ;
static constexpr const char * OBJECT_UUID_SUFFIX = " -61cb-4c03-9d28-80fed5dfa1dc " ;
static constexpr const char * OBJECT_UUID_SUFFIX2 = " -71cb-4c03-9d28-80fed5dfa1dc " ;
static constexpr const char * SUB_OBJECT_UUID_SUFFIX = " -81cb-4c03-9d28-80fed5dfa1dc " ;
static constexpr const char * COMPONENT_UUID_SUFFIX = " -b206-40ff-9872-83e8017abed1 " ;
static constexpr const char * BUILD_UUID = " 2c7c17d8-22b5-4d84-8835-1976022ea369 " ;
static constexpr const char * BUILD_UUID_SUFFIX = " -b1ec-4553-aec9-835e5b724bb4 " ;
static constexpr const char * TARGET_ATTR = " Target " ;
static constexpr const char * RELS_TYPE_ATTR = " Type " ;
static constexpr const char * UNIT_ATTR = " unit " ;
static constexpr const char * NAME_ATTR = " name " ;
static constexpr const char * COLOR_ATTR = " color " ;
static constexpr const char * TYPE_ATTR = " type " ;
static constexpr const char * ID_ATTR = " id " ;
static constexpr const char * X_ATTR = " x " ;
static constexpr const char * Y_ATTR = " y " ;
static constexpr const char * Z_ATTR = " z " ;
static constexpr const char * V1_ATTR = " v1 " ;
static constexpr const char * V2_ATTR = " v2 " ;
static constexpr const char * V3_ATTR = " v3 " ;
static constexpr const char * OBJECTID_ATTR = " objectid " ;
static constexpr const char * TRANSFORM_ATTR = " transform " ;
// QDS
static constexpr const char * OFFSET_ATTR = " offset " ;
static constexpr const char * PRINTABLE_ATTR = " printable " ;
static constexpr const char * INSTANCESCOUNT_ATTR = " instances_count " ;
static constexpr const char * CUSTOM_SUPPORTS_ATTR = " paint_supports " ;
static constexpr const char * CUSTOM_SEAM_ATTR = " paint_seam " ;
static constexpr const char * MMU_SEGMENTATION_ATTR = " paint_color " ;
// QDS
static constexpr const char * FACE_PROPERTY_ATTR = " face_property " ;
static constexpr const char * KEY_ATTR = " key " ;
static constexpr const char * VALUE_ATTR = " value " ;
static constexpr const char * FIRST_TRIANGLE_ID_ATTR = " firstid " ;
static constexpr const char * LAST_TRIANGLE_ID_ATTR = " lastid " ;
static constexpr const char * SUBTYPE_ATTR = " subtype " ;
static constexpr const char * LOCK_ATTR = " locked " ;
static constexpr const char * BED_TYPE_ATTR = " bed_type " ;
static constexpr const char * PRINT_SEQUENCE_ATTR = " print_sequence " ;
static constexpr const char * FIRST_LAYER_PRINT_SEQUENCE_ATTR = " first_layer_print_sequence " ;
static constexpr const char * OTHER_LAYERS_PRINT_SEQUENCE_ATTR = " other_layers_print_sequence " ;
static constexpr const char * OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR = " other_layers_print_sequence_nums " ;
static constexpr const char * SPIRAL_VASE_MODE = " spiral_mode " ;
static constexpr const char * GCODE_FILE_ATTR = " gcode_file " ;
static constexpr const char * THUMBNAIL_FILE_ATTR = " thumbnail_file " ;
static constexpr const char * NO_LIGHT_THUMBNAIL_FILE_ATTR = " thumbnail_no_light_file " ;
static constexpr const char * TOP_FILE_ATTR = " top_file " ;
static constexpr const char * PICK_FILE_ATTR = " pick_file " ;
static constexpr const char * PATTERN_FILE_ATTR = " pattern_file " ;
static constexpr const char * PATTERN_BBOX_FILE_ATTR = " pattern_bbox_file " ;
static constexpr const char * OBJECT_ID_ATTR = " object_id " ;
static constexpr const char * INSTANCEID_ATTR = " instance_id " ;
static constexpr const char * IDENTIFYID_ATTR = " identify_id " ;
static constexpr const char * PLATERID_ATTR = " plater_id " ;
static constexpr const char * PLATER_NAME_ATTR = " plater_name " ;
static constexpr const char * PLATE_IDX_ATTR = " index " ;
static constexpr const char * PRINTER_MODEL_ID_ATTR = " printer_model_id " ;
static constexpr const char * NOZZLE_DIAMETERS_ATTR = " nozzle_diameters " ;
static constexpr const char * SLICE_PREDICTION_ATTR = " prediction " ;
static constexpr const char * SLICE_WEIGHT_ATTR = " weight " ;
static constexpr const char * TIMELAPSE_TYPE_ATTR = " timelapse_type " ;
static constexpr const char * TIMELAPSE_ERROR_CODE_ATTR = " timelapse_error_code " ;
static constexpr const char * OUTSIDE_ATTR = " outside " ;
static constexpr const char * SUPPORT_USED_ATTR = " support_used " ;
static constexpr const char * LABEL_OBJECT_ENABLED_ATTR = " label_object_enabled " ;
static constexpr const char * SKIPPED_ATTR = " skipped " ;
static constexpr const char * OBJECT_TYPE = " object " ;
static constexpr const char * VOLUME_TYPE = " volume " ;
static constexpr const char * PART_TYPE = " part " ;
static constexpr const char * NAME_KEY = " name " ;
static constexpr const char * VOLUME_TYPE_KEY = " volume_type " ;
static constexpr const char * PART_TYPE_KEY = " part_type " ;
static constexpr const char * MATRIX_KEY = " matrix " ;
static constexpr const char * SOURCE_FILE_KEY = " source_file " ;
static constexpr const char * SOURCE_OBJECT_ID_KEY = " source_object_id " ;
static constexpr const char * SOURCE_VOLUME_ID_KEY = " source_volume_id " ;
static constexpr const char * SOURCE_OFFSET_X_KEY = " source_offset_x " ;
static constexpr const char * SOURCE_OFFSET_Y_KEY = " source_offset_y " ;
static constexpr const char * SOURCE_OFFSET_Z_KEY = " source_offset_z " ;
static constexpr const char * SOURCE_IN_INCHES = " source_in_inches " ;
static constexpr const char * SOURCE_IN_METERS = " source_in_meters " ;
static constexpr const char * MESH_SHARED_KEY = " mesh_shared " ;
static constexpr const char * MESH_STAT_EDGES_FIXED = " edges_fixed " ;
static constexpr const char * MESH_STAT_DEGENERATED_FACETS = " degenerate_facets " ;
static constexpr const char * MESH_STAT_FACETS_REMOVED = " facets_removed " ;
static constexpr const char * MESH_STAT_FACETS_RESERVED = " facets_reversed " ;
static constexpr const char * MESH_STAT_BACKWARDS_EDGES = " backwards_edges " ;
const unsigned int QDS_VALID_OBJECT_TYPES_COUNT = 2 ;
const char * QDS_VALID_OBJECT_TYPES [ ] =
{
" model " ,
" other "
} ;
const char * QDS_INVALID_OBJECT_TYPES [ ] =
{
" solidsupport " ,
" support " ,
" surface "
} ;
template < typename T >
struct hex_wrap
{
T t ;
} ;
namespace std {
template < class _Elem , class _Traits , class _Arg >
basic_ostream < _Elem , _Traits > & operator < < ( basic_ostream < _Elem , _Traits > & ostr ,
const hex_wrap < _Arg > & wrap ) { // insert by calling function with output stream and argument
auto of = ostr . fill ( ' 0 ' ) ;
ostr < < setw ( sizeof ( _Arg ) * 2 ) < < std : : hex < < wrap . t ;
ostr < < std : : dec < < setw ( 0 ) ;
ostr . fill ( of ) ;
return ostr ;
}
}
class version_error : public Slic3r : : FileIOError
{
public :
version_error ( const std : : string & what_arg ) : Slic3r : : FileIOError ( what_arg ) { }
version_error ( const char * what_arg ) : Slic3r : : FileIOError ( what_arg ) { }
} ;
const char * qds_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 qds_get_attribute_value_string ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
const char * text = qds_get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ;
return ( text ! = nullptr ) ? text : " " ;
}
float qds_get_attribute_value_float ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
float value = 0.0f ;
if ( const char * text = qds_get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ; text ! = nullptr )
fast_float : : from_chars ( text , text + strlen ( text ) , value ) ;
return value ;
}
int qds_get_attribute_value_int ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
int value = 0 ;
if ( const char * text = qds_get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ; text ! = nullptr )
boost : : spirit : : qi : : parse ( text , text + strlen ( text ) , boost : : spirit : : qi : : int_ , value ) ;
return value ;
}
bool qds_get_attribute_value_bool ( const char * * attributes , unsigned int attributes_size , const char * attribute_key )
{
const char * text = qds_get_attribute_value_charptr ( attributes , attributes_size , attribute_key ) ;
return ( text ! = nullptr ) ? ( bool ) : : atoi ( text ) : true ;
}
void add_vec3 ( std : : stringstream & stream , const Slic3r : : Vec3f & tr )
{
for ( unsigned r = 0 ; r < 3 ; + + r ) {
stream < < tr ( r ) ;
if ( r ! = 2 )
stream < < " " ;
}
}
Slic3r : : Vec3f get_vec3_from_string ( const std : : string & pos_str )
{
Slic3r : : Vec3f pos ( 0 , 0 , 0 ) ;
if ( pos_str . empty ( ) )
return pos ;
std : : vector < std : : string > values ;
boost : : split ( values , pos_str , boost : : is_any_of ( " " ) , boost : : token_compress_on ) ;
if ( values . size ( ) ! = 3 )
return pos ;
for ( int i = 0 ; i < 3 ; + + i )
pos ( i ) = : : atof ( values [ i ] . c_str ( ) ) ;
return pos ;
}
Slic3r : : Transform3d qds_get_transform_from_3mf_specs_string ( const std : : string & mat_str )
{
// 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 ( ) ;
if ( mat_str . empty ( ) )
// empty string means default identity matrix
return ret ;
std : : vector < std : : string > mat_elements_str ;
boost : : split ( mat_elements_str , mat_str , boost : : is_any_of ( " " ) , boost : : token_compress_on ) ;
unsigned int size = ( unsigned int ) mat_elements_str . size ( ) ;
if ( size ! = 12 )
// invalid data, return identity matrix
return ret ;
unsigned int i = 0 ;
// matrices are stored into 3mf files as 4x3
// we need to transpose them
for ( unsigned int c = 0 ; c < 4 ; + + c ) {
for ( unsigned int r = 0 ; r < 3 ; + + r ) {
ret ( r , c ) = : : atof ( mat_elements_str [ i + + ] . c_str ( ) ) ;
}
}
return ret ;
}
Slic3r : : Vec3d qds_get_offset_from_3mf_specs_string ( const std : : string & vec_str )
{
Slic3r : : Vec3d ofs2ass ( 0 , 0 , 0 ) ;
if ( vec_str . empty ( ) )
// empty string means default zero offset
return ofs2ass ;
std : : vector < std : : string > vec_elements_str ;
boost : : split ( vec_elements_str , vec_str , boost : : is_any_of ( " " ) , boost : : token_compress_on ) ;
unsigned int size = ( unsigned int ) vec_elements_str . size ( ) ;
if ( size ! = 3 )
// invalid data, return zero offset
return ofs2ass ;
for ( unsigned int i = 0 ; i < 3 ; i + + ) {
ofs2ass ( i ) = : : atof ( vec_elements_str [ i ] . c_str ( ) ) ;
}
return ofs2ass ;
}
float qds_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 qds_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 < QDS_VALID_OBJECT_TYPES_COUNT ; + + i ) {
if ( : : strcmp ( type . c_str ( ) , QDS_VALID_OBJECT_TYPES [ i ] ) = = 0 )
return true ;
}
return false ;
}
namespace Slic3r {
void PlateData : : parse_filament_info ( GCodeProcessorResult * result )
{
if ( ! result ) return ;
PrintEstimatedStatistics & ps = result - > print_statistics ;
std : : vector < float > m_filament_diameters = result - > filament_diameters ;
std : : vector < float > m_filament_densities = result - > filament_densities ;
auto get_used_filament_from_volume = [ m_filament_diameters , m_filament_densities ] ( double volume , int extruder_id ) {
double koef = 0.001 ;
std : : pair < double , double > ret = { koef * volume / ( PI * sqr ( 0.5 * m_filament_diameters [ extruder_id ] ) ) , volume * m_filament_densities [ extruder_id ] * 0.001 } ;
return ret ;
} ;
for ( auto it = ps . total_volumes_per_extruder . begin ( ) ; it ! = ps . total_volumes_per_extruder . end ( ) ; it + + ) {
double volume = it - > second ;
auto [ used_filament_m , used_filament_g ] = get_used_filament_from_volume ( volume , it - > first ) ;
FilamentInfo info ;
info . id = it - > first ;
info . used_g = used_filament_g ;
info . used_m = used_filament_m ;
slice_filaments_info . push_back ( info ) ;
}
/* only for test
GCodeProcessorResult : : SliceWarning sw ;
sw . msg = BED_TEMP_TOO_HIGH_THAN_FILAMENT ;
sw . level = 1 ;
result - > warnings . push_back ( sw ) ;
*/
warnings = result - > warnings ;
}
//! macro used to mark string used at localization,
//! return same string
# define L(s) (s)
# define _(s) Slic3r::I18N::translate(s)
// Base class with error messages management
class _QDS_3MF_Base
{
mutable boost : : mutex mutex ;
mutable std : : vector < std : : string > m_errors ;
protected :
void add_error ( const std : : string & error ) const { boost : : unique_lock l ( mutex ) ; m_errors . push_back ( error ) ; }
void clear_errors ( ) { m_errors . clear ( ) ; }
public :
void log_errors ( )
{
for ( const std : : string & error : m_errors )
BOOST_LOG_TRIVIAL ( error ) < < error ;
}
} ;
class _QDS_3MF_Importer : public _QDS_3MF_Base
{
typedef std : : pair < std : : string , int > Id ; // QDS: encrypt
struct Component
{
Id object_id ;
Transform3d transform ;
explicit Component ( Id object_id )
: object_id ( object_id )
, transform ( Transform3d : : Identity ( ) )
{
}
Component ( Id object_id , const Transform3d & transform )
: object_id ( object_id )
, transform ( transform )
{
}
} ;
typedef std : : vector < Component > ComponentsList ;
struct Geometry
{
std : : vector < Vec3f > vertices ;
std : : vector < Vec3i > triangles ;
std : : vector < std : : string > custom_supports ;
std : : vector < std : : string > custom_seam ;
std : : vector < std : : string > mmu_segmentation ;
// QDS
std : : vector < std : : string > face_properties ;
bool empty ( ) { return vertices . empty ( ) | | triangles . empty ( ) ; }
// backup & restore
void swap ( Geometry & o ) {
std : : swap ( vertices , o . vertices ) ;
std : : swap ( triangles , o . triangles ) ;
std : : swap ( custom_supports , o . custom_supports ) ;
std : : swap ( custom_seam , o . custom_seam ) ;
}
void reset ( ) {
vertices . clear ( ) ;
triangles . clear ( ) ;
custom_supports . clear ( ) ;
custom_seam . clear ( ) ;
mmu_segmentation . clear ( ) ;
}
} ;
struct CurrentObject
{
// ID of the object inside the 3MF file, 1 based.
int id ;
// Index of the ModelObject in its respective Model, zero based.
int model_object_idx ;
Geometry geometry ;
ModelObject * object ;
ComponentsList components ;
//QDS: sub object id
//int subobject_id;
std : : string name ;
std : : string uuid ;
int pid { - 1 } ;
//bool is_model_object;
CurrentObject ( ) { reset ( ) ; }
void reset ( ) {
id = - 1 ;
model_object_idx = - 1 ;
geometry . reset ( ) ;
object = nullptr ;
components . clear ( ) ;
//QDS: sub object id
uuid . clear ( ) ;
name . clear ( ) ;
}
} ;
struct CurrentConfig
{
int object_id { - 1 } ;
int volume_id { - 1 } ;
} ;
struct CurrentInstance
{
int object_id ;
int instance_id ;
int identify_id ;
} ;
struct Instance
{
ModelInstance * instance ;
Transform3d transform ;
Instance ( ModelInstance * instance , const Transform3d & transform )
: instance ( instance )
, transform ( transform )
{
}
} ;
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
{
//QDS: refine the part logic
unsigned int first_triangle_id ;
unsigned int last_triangle_id ;
int subobject_id ;
MetadataList metadata ;
RepairedMeshErrors mesh_stats ;
ModelVolumeType part_type ;
TextInfo text_info ;
VolumeMetadata ( unsigned int first_triangle_id , unsigned int last_triangle_id , ModelVolumeType type = ModelVolumeType : : MODEL_PART )
: first_triangle_id ( first_triangle_id )
, last_triangle_id ( last_triangle_id )
, part_type ( type )
, subobject_id ( - 1 )
{
}
VolumeMetadata ( int sub_id , ModelVolumeType type = ModelVolumeType : : MODEL_PART )
: subobject_id ( sub_id )
, part_type ( type )
, first_triangle_id ( 0 )
, last_triangle_id ( 0 )
{
}
} ;
typedef std : : vector < VolumeMetadata > VolumeMetadataList ;
MetadataList metadata ;
VolumeMetadataList volumes ;
} ;
struct CutObjectInfo
{
struct Connector
{
int volume_id ;
int type ;
float radius ;
float height ;
float r_tolerance ;
float h_tolerance ;
} ;
CutObjectBase id ;
std : : vector < Connector > connectors ;
} ;
// Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
//typedef std::pair<std::string, int> Id; // QDS: encrypt
typedef std : : map < Id , CurrentObject > IdToCurrentObjectMap ;
typedef std : : map < int , std : : string > IndexToPathMap ;
typedef std : : map < Id , int > IdToModelObjectMap ;
//typedef std::map<Id, ComponentsList> IdToAliasesMap;
typedef std : : vector < Instance > InstancesList ;
typedef std : : map < int , ObjectMetadata > IdToMetadataMap ;
typedef std : : map < int , CutObjectInfo > IdToCutObjectInfoMap ;
//typedef std::map<Id, Geometry> IdToGeometryMap;
typedef std : : map < int , std : : vector < coordf_t > > IdToLayerHeightsProfileMap ;
typedef std : : map < int , t_layer_config_ranges > IdToLayerConfigRangesMap ;
/*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
typedef std : : map < int , std : : vector < sla : : DrainHole > > IdToSlaDrainHolesMap ; */
struct ObjectImporter
{
IdToCurrentObjectMap object_list ;
CurrentObject * current_object { nullptr } ;
std : : string object_path ;
std : : string zip_path ;
_QDS_3MF_Importer * top_importer { nullptr } ;
XML_Parser object_xml_parser ;
bool obj_parse_error { false } ;
std : : string obj_parse_error_message ;
//local parsed datas
std : : string obj_curr_metadata_name ;
std : : string obj_curr_characters ;
float object_unit_factor ;
int object_current_color_group { - 1 } ;
std : : map < int , std : : string > object_group_id_to_color ;
bool is_qdt_3mf { false } ;
ObjectImporter ( _QDS_3MF_Importer * importer , std : : string file_path , std : : string obj_path )
{
top_importer = importer ;
object_path = obj_path ;
zip_path = file_path ;
}
~ ObjectImporter ( )
{
_destroy_object_xml_parser ( ) ;
}
void _destroy_object_xml_parser ( )
{
if ( object_xml_parser ! = nullptr ) {
XML_ParserFree ( object_xml_parser ) ;
object_xml_parser = nullptr ;
}
}
void _stop_object_xml_parser ( const std : : string & msg = std : : string ( ) )
{
assert ( ! obj_parse_error ) ;
assert ( obj_parse_error_message . empty ( ) ) ;
assert ( object_xml_parser ! = nullptr ) ;
obj_parse_error = true ;
obj_parse_error_message = msg ;
XML_StopParser ( object_xml_parser , false ) ;
}
bool object_parse_error ( ) const { return obj_parse_error ; }
const char * object_parse_error_message ( ) const {
return obj_parse_error ?
// The error was signalled by the user code, not the expat parser.
( obj_parse_error_message . empty ( ) ? " Invalid 3MF format " : obj_parse_error_message . c_str ( ) ) :
// The error was signalled by the expat parser.
XML_ErrorString ( XML_GetErrorCode ( object_xml_parser ) ) ;
}
bool _extract_object_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
bool extract_object_model ( )
{
mz_zip_archive archive ;
mz_zip_archive_file_stat stat ;
mz_zip_zero_struct ( & archive ) ;
if ( ! open_zip_reader ( & archive , zip_path ) ) {
top_importer - > add_error ( " Unable to open the zipfile " + zip_path ) ;
return false ;
}
if ( ! top_importer - > _extract_from_archive ( archive , object_path , [ this ] ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) {
return _extract_object_from_archive ( archive , stat ) ;
} , top_importer - > m_load_restore ) ) {
std : : string error_msg = std : : string ( " Archive does not contain a valid model for " ) + object_path ;
top_importer - > add_error ( error_msg ) ;
close_zip_reader ( & archive ) ;
return false ;
}
close_zip_reader ( & archive ) ;
if ( obj_parse_error ) {
//already add_error inside
//top_importer->add_error(object_parse_error_message());
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Found error while extrace object %1% \n " ) % object_path ;
return false ;
}
return true ;
}
bool _handle_object_start_model ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_model ( ) ;
bool _handle_object_start_resources ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_resources ( ) ;
bool _handle_object_start_object ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_object ( ) ;
bool _handle_object_start_color_group ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_color_group ( ) ;
bool _handle_object_start_color ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_color ( ) ;
bool _handle_object_start_mesh ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_mesh ( ) ;
bool _handle_object_start_vertices ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_vertices ( ) ;
bool _handle_object_start_vertex ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_vertex ( ) ;
bool _handle_object_start_triangles ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_triangles ( ) ;
bool _handle_object_start_triangle ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_triangle ( ) ;
bool _handle_object_start_components ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_components ( ) ;
bool _handle_object_start_component ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_component ( ) ;
bool _handle_object_start_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_object_end_metadata ( ) ;
void _handle_object_start_model_xml_element ( const char * name , const char * * attributes ) ;
void _handle_object_end_model_xml_element ( const char * name ) ;
void _handle_object_xml_characters ( const XML_Char * s , int len ) ;
// callbacks to parse the .model file of an object
static void XMLCALL _handle_object_start_model_xml_element ( void * userData , const char * name , const char * * attributes ) ;
static void XMLCALL _handle_object_end_model_xml_element ( void * userData , const char * name ) ;
static void XMLCALL _handle_object_xml_characters ( void * userData , const XML_Char * s , int len ) ;
} ;
// Version of the 3mf file
unsigned int m_version ;
bool m_check_version = false ;
bool m_load_model = false ;
bool m_load_aux = false ;
bool m_load_config = false ;
// backup & restore
bool m_load_restore = false ;
std : : string m_backup_path ;
std : : string m_origin_file ;
// Semantic version of QIDI Studio, that generated this 3MF.
boost : : optional < Semver > m_qidislicer_generator_version ;
unsigned int m_fdm_supports_painting_version = 0 ;
unsigned int m_seam_painting_version = 0 ;
unsigned int m_mm_painting_version = 0 ;
std : : string m_model_id ;
std : : string m_contry_code ;
std : : string m_designer ;
std : : string m_designer_id ;
std : : string m_design_id ;
std : : string m_designer_user_id ;
std : : string m_designer_cover ;
ModelInfo model_info ;
QDTProject project_info ;
std : : string m_profile_title ;
std : : string m_profile_cover ;
std : : string m_Profile_description ;
std : : string m_profile_user_id ;
std : : string m_profile_user_name ;
XML_Parser m_xml_parser ;
// Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state
// after returning from XML_Parse() function, thus we keep the error state here.
bool m_parse_error { false } ;
std : : string m_parse_error_message ;
Model * m_model ;
float m_unit_factor ;
CurrentObject * m_curr_object { nullptr } ;
IdToCurrentObjectMap m_current_objects ;
IndexToPathMap m_index_paths ;
IdToModelObjectMap m_objects ;
//IdToAliasesMap m_objects_aliases;
InstancesList m_instances ;
//IdToGeometryMap m_geometries;
//IdToGeometryMap m_orig_geometries; // backup & restore
CurrentConfig m_curr_config ;
IdToMetadataMap m_objects_metadata ;
IdToCutObjectInfoMap m_cut_object_infos ;
IdToLayerHeightsProfileMap m_layer_heights_profiles ;
IdToLayerConfigRangesMap m_layer_config_ranges ;
/*IdToSlaSupportPointsMap m_sla_support_points;
IdToSlaDrainHolesMap m_sla_drain_holes ; */
std : : string m_curr_metadata_name ;
std : : string m_curr_characters ;
std : : string m_name ;
std : : string m_sub_model_path ;
std : : string m_start_part_path ;
std : : string m_thumbnail_path ;
std : : string m_thumbnail_middle ;
std : : string m_thumbnail_small ;
std : : vector < std : : string > m_sub_model_paths ;
std : : vector < ObjectImporter * > m_object_importers ;
std : : map < int , ModelVolume * > m_shared_meshes ;
//QDS: plater related structures
bool m_is_qdt_3mf { false } ;
bool m_parsing_slice_info { false } ;
PlateDataMaps m_plater_data ;
PlateData * m_curr_plater ;
CurrentInstance m_curr_instance ;
int m_current_color_group { - 1 } ;
std : : map < int , std : : string > m_group_id_to_color ;
public :
_QDS_3MF_Importer ( ) ;
~ _QDS_3MF_Importer ( ) ;
//QDS: add plate data related logic
// add backup & restore logic
bool load_model_from_file ( const std : : string & filename , Model & model , PlateDataPtrs & plate_data_list , std : : vector < Preset * > & project_presets , DynamicPrintConfig & config ,
ConfigSubstitutionContext & config_substitutions , LoadStrategy strategy , bool & is_qdt_3mf , Semver & file_version , Import3mfProgressFn proFn = nullptr , QDTProject * project = nullptr , int plate_id = 0 ) ;
bool get_thumbnail ( const std : : string & filename , std : : string & data ) ;
bool load_gcode_3mf_from_stream ( std : : istream & data , Model & model , PlateDataPtrs & plate_data_list , DynamicPrintConfig & config , Semver & file_version ) ;
unsigned int version ( ) const { return m_version ; }
private :
void _destroy_xml_parser ( ) ;
void _stop_xml_parser ( const std : : string & msg = std : : string ( ) ) ;
bool parse_error ( ) const { return m_parse_error ; }
const char * parse_error_message ( ) const {
return m_parse_error ?
// The error was signalled by the user code, not the expat parser.
( m_parse_error_message . empty ( ) ? " Invalid 3MF format " : m_parse_error_message . c_str ( ) ) :
// The error was signalled by the expat parser.
XML_ErrorString ( XML_GetErrorCode ( m_xml_parser ) ) ;
}
//QDS: add plate data related logic
// add backup & restore logic
bool _load_model_from_file ( std : : string filename , Model & model , PlateDataPtrs & plate_data_list , std : : vector < Preset * > & project_presets , DynamicPrintConfig & config , ConfigSubstitutionContext & config_substitutions , Import3mfProgressFn proFn = nullptr ,
QDTProject * project = nullptr , int plate_id = 0 ) ;
bool _extract_from_archive ( mz_zip_archive & archive , std : : string const & path , std : : function < bool ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) > , bool restore = false ) ;
bool _extract_xml_from_archive ( mz_zip_archive & archive , std : : string const & path , XML_StartElementHandler start_handler , XML_EndElementHandler end_handler ) ;
bool _extract_xml_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , XML_StartElementHandler start_handler , XML_EndElementHandler end_handler ) ;
bool _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
void _extract_cut_information_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , ConfigSubstitutionContext & config_substitutions ) ;
void _extract_layer_heights_profile_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
void _extract_layer_config_ranges_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , ConfigSubstitutionContext & config_substitutions ) ;
void _extract_sla_support_points_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
void _extract_sla_drain_holes_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
void _extract_custom_gcode_per_print_z_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
void _extract_print_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig & config , ConfigSubstitutionContext & subs_context , const std : : string & archive_filename ) ;
//QDS: add project config file logic
void _extract_project_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig & config , ConfigSubstitutionContext & subs_context , Model & model ) ;
//QDS: extract project embedded presets
void _extract_project_embedded_presets_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , std : : vector < Preset * > & project_presets , Model & model , Preset : : Type type , bool use_json = true ) ;
void _extract_auxiliary_file_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model ) ;
void _extract_file_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) ;
// 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 ) ;
void _handle_xml_characters ( const XML_Char * s , int len ) ;
// 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 ) ;
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_color_group ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_color_group ( ) ;
bool _handle_start_color ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_color ( ) ;
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 ( ) ;
bool _handle_start_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_metadata ( ) ;
bool _create_object_instance ( std : : string const & path , int object_id , const Transform3d & transform , const bool printable , unsigned int recur_counter ) ;
void _apply_transform ( ModelInstance & instance , const Transform3d & transform ) ;
bool _handle_start_config ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config ( ) ;
bool _handle_start_config_object ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_object ( ) ;
bool _handle_start_config_volume ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_start_config_volume_mesh ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_volume ( ) ;
bool _handle_end_config_volume_mesh ( ) ;
bool _handle_start_config_metadata ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_metadata ( ) ;
bool _handle_start_config_filament ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_filament ( ) ;
bool _handle_start_config_warning ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_warning ( ) ;
//QDS: add plater config parse functions
bool _handle_start_config_plater ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_plater ( ) ;
bool _handle_start_config_plater_instance ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_config_plater_instance ( ) ;
bool _handle_start_assemble ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_assemble ( ) ;
bool _handle_start_assemble_item ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_assemble_item ( ) ;
bool _handle_start_text_info_item ( const char * * attributes , unsigned int num_attributes ) ;
bool _handle_end_text_info_item ( ) ;
// QDS: callbacks to parse the .rels file
static void XMLCALL _handle_start_relationships_element ( void * userData , const char * name , const char * * attributes ) ;
static void XMLCALL _handle_end_relationships_element ( void * userData , const char * name ) ;
void _handle_start_relationships_element ( const char * name , const char * * attributes ) ;
void _handle_end_relationships_element ( const char * name ) ;
bool _handle_start_relationship ( const char * * attributes , unsigned int num_attributes ) ;
void _generate_current_object_list ( std : : vector < Component > & sub_objects , Id object_id , IdToCurrentObjectMap & current_objects ) ;
bool _generate_volumes_new ( ModelObject & object , const std : : vector < Component > & sub_objects , const ObjectMetadata : : VolumeMetadataList & volumes , ConfigSubstitutionContext & config_substitutions ) ;
bool _generate_volumes ( ModelObject & object , const Geometry & geometry , const ObjectMetadata : : VolumeMetadataList & volumes , ConfigSubstitutionContext & config_substitutions ) ;
// 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 ) ;
static void XMLCALL _handle_xml_characters ( void * userData , const XML_Char * s , int len ) ;
// 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 ) ;
} ;
_QDS_3MF_Importer : : _QDS_3MF_Importer ( )
: m_version ( 0 )
, m_check_version ( false )
, m_xml_parser ( nullptr )
, m_model ( nullptr )
, m_unit_factor ( 1.0f )
, m_curr_metadata_name ( " " )
, m_curr_characters ( " " )
, m_name ( " " )
, m_curr_plater ( nullptr )
{
}
_QDS_3MF_Importer : : ~ _QDS_3MF_Importer ( )
{
_destroy_xml_parser ( ) ;
clear_errors ( ) ;
if ( m_curr_object ) {
delete m_curr_object ;
m_curr_object = nullptr ;
}
m_current_objects . clear ( ) ;
m_index_paths . clear ( ) ;
m_objects . clear ( ) ;
m_instances . clear ( ) ;
m_objects_metadata . clear ( ) ;
m_curr_metadata_name . clear ( ) ;
m_curr_characters . clear ( ) ;
std : : map < int , PlateData * > : : iterator it = m_plater_data . begin ( ) ;
while ( it ! = m_plater_data . end ( ) )
{
delete it - > second ;
it + + ;
}
m_plater_data . clear ( ) ;
}
//QDS: add plate data related logic
// add backup & restore logic
bool _QDS_3MF_Importer : : load_model_from_file ( const std : : string & filename , Model & model , PlateDataPtrs & plate_data_list , std : : vector < Preset * > & project_presets , DynamicPrintConfig & config ,
ConfigSubstitutionContext & config_substitutions , LoadStrategy strategy , bool & is_qdt_3mf , Semver & file_version , Import3mfProgressFn proFn , QDTProject * project , int plate_id )
{
m_version = 0 ;
m_fdm_supports_painting_version = 0 ;
m_seam_painting_version = 0 ;
m_mm_painting_version = 0 ;
m_check_version = strategy & LoadStrategy : : CheckVersion ;
//QDS: auxiliary data
m_load_model = strategy & LoadStrategy : : LoadModel ;
m_load_aux = strategy & LoadStrategy : : LoadAuxiliary ;
m_load_restore = strategy & LoadStrategy : : Restore ;
m_load_config = strategy & LoadStrategy : : LoadConfig ;
m_model = & model ;
m_unit_factor = 1.0f ;
m_curr_object = nullptr ;
m_current_objects . clear ( ) ;
m_index_paths . clear ( ) ;
m_objects . clear ( ) ;
//m_objects_aliases.clear();
m_instances . clear ( ) ;
//m_geometries.clear();
m_curr_config . object_id = - 1 ;
m_curr_config . volume_id = - 1 ;
m_objects_metadata . clear ( ) ;
m_layer_heights_profiles . clear ( ) ;
m_layer_config_ranges . clear ( ) ;
//m_sla_support_points.clear();
m_curr_metadata_name . clear ( ) ;
m_curr_characters . clear ( ) ;
//QDS: plater data init
m_plater_data . clear ( ) ;
m_curr_instance . object_id = - 1 ;
m_curr_instance . instance_id = - 1 ;
m_curr_instance . identify_id = 0 ;
clear_errors ( ) ;
// restore
if ( m_load_restore ) {
m_backup_path = filename . substr ( 0 , filename . size ( ) - 5 ) ;
model . set_backup_path ( m_backup_path ) ;
try {
if ( boost : : filesystem : : exists ( model . get_backup_path ( ) + " /origin.txt " ) )
boost : : filesystem : : load_string_file ( model . get_backup_path ( ) + " /origin.txt " , m_origin_file ) ;
} catch ( . . . ) { }
boost : : filesystem : : save_string_file (
model . get_backup_path ( ) + " /lock.txt " ,
boost : : lexical_cast < std : : string > ( get_current_pid ( ) ) ) ;
}
else {
m_backup_path = model . get_backup_path ( ) ;
}
bool result = _load_model_from_file ( filename , model , plate_data_list , project_presets , config , config_substitutions , proFn , project , plate_id ) ;
is_qdt_3mf = m_is_qdt_3mf ;
if ( m_qidislicer_generator_version )
file_version = * m_qidislicer_generator_version ;
// save for restore
if ( result & & m_load_aux & & ! m_load_restore ) {
boost : : filesystem : : save_string_file ( model . get_backup_path ( ) + " /origin.txt " , filename ) ;
}
if ( m_load_restore & & ! result ) // not clear failed backup data for later analyze
model . set_backup_path ( " detach " ) ;
return result ;
}
bool _QDS_3MF_Importer : : get_thumbnail ( const std : : string & filename , std : : string & data )
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
struct close_lock
{
mz_zip_archive * archive ;
void close ( )
{
if ( archive ) {
close_zip_reader ( archive ) ;
archive = nullptr ;
}
}
~ close_lock ( ) { close ( ) ; }
} lock { & archive } ;
if ( ! open_zip_reader ( & archive , filename ) ) {
add_error ( " Unable to open the file " + filename ) ;
return false ;
}
// QDS: load relationships
if ( ! _extract_xml_from_archive ( archive , RELATIONSHIPS_FILE , _handle_start_relationships_element , _handle_end_relationships_element ) )
return false ;
if ( m_thumbnail_middle . empty ( ) ) m_thumbnail_middle = m_thumbnail_path ;
if ( ! m_thumbnail_middle . empty ( ) ) {
return _extract_from_archive ( archive , m_thumbnail_middle , [ & data ] ( auto & archive , auto const & stat ) - > bool {
data . resize ( stat . m_uncomp_size ) ;
return mz_zip_reader_extract_to_mem ( & archive , stat . m_file_index , data . data ( ) , data . size ( ) , 0 ) ;
} ) ;
}
return _extract_from_archive ( archive , THUMBNAIL_FILE , [ & data ] ( auto & archive , auto const & stat ) - > bool {
data . resize ( stat . m_uncomp_size ) ;
return mz_zip_reader_extract_to_mem ( & archive , stat . m_file_index , data . data ( ) , data . size ( ) , 0 ) ;
} ) ;
}
static size_t mz_zip_read_istream ( void * pOpaque , mz_uint64 file_ofs , void * pBuf , size_t n )
{
auto & is = * reinterpret_cast < std : : istream * > ( pOpaque ) ;
is . seekg ( file_ofs , std : : istream : : beg ) ;
if ( ! is )
return 0 ;
is . read ( ( char * ) pBuf , n ) ;
return is . gcount ( ) ;
}
bool _QDS_3MF_Importer : : load_gcode_3mf_from_stream ( std : : istream & data , Model & model , PlateDataPtrs & plate_data_list , DynamicPrintConfig & config , Semver & file_version )
{
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
archive . m_pRead = mz_zip_read_istream ;
archive . m_pIO_opaque = & data ;
data . seekg ( 0 , std : : istream : : end ) ;
mz_uint64 size = data . tellg ( ) ;
data . seekg ( 0 , std : : istream : : beg ) ;
if ( ! mz_zip_reader_init ( & archive , size , 0 ) )
return false ;
struct close_lock
{
mz_zip_archive * archive ;
void close ( )
{
if ( archive ) {
mz_zip_reader_end ( archive ) ;
archive = nullptr ;
}
}
~ close_lock ( ) { close ( ) ; }
} lock { & archive } ;
// QDS: load relationships
if ( ! _extract_xml_from_archive ( archive , RELATIONSHIPS_FILE , _handle_start_relationships_element , _handle_end_relationships_element ) )
return false ;
if ( m_start_part_path . empty ( ) )
return false ;
//extract model files
m_model = & model ;
if ( ! _extract_from_archive ( archive , m_start_part_path , [ this ] ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) {
return _extract_model_from_archive ( archive , stat ) ;
} ) ) {
add_error ( " Archive does not contain a valid model " ) ;
return false ;
}
if ( ! m_designer . empty ( ) ) {
m_model - > design_info = std : : make_shared < ModelDesignInfo > ( ) ;
m_model - > design_info - > DesignerId = m_designer_id ;
m_model - > design_info - > DesignerUserId = m_designer_user_id ;
m_model - > design_info - > Designer = m_designer ;
}
m_model - > profile_info = std : : make_shared < ModelProfileInfo > ( ) ;
m_model - > profile_info - > ProfileTile = m_profile_title ;
m_model - > profile_info - > ProfileCover = m_profile_cover ;
m_model - > profile_info - > ProfileDescription = m_Profile_description ;
m_model - > profile_info - > ProfileUserId = m_profile_user_id ;
m_model - > profile_info - > ProfileUserName = m_profile_user_name ;
m_model - > design_id = m_design_id ;
m_model - > model_info = std : : make_shared < ModelInfo > ( ) ;
m_model - > model_info - > load ( model_info ) ;
if ( m_thumbnail_middle . empty ( ) ) m_thumbnail_middle = m_thumbnail_path ;
if ( m_thumbnail_small . empty ( ) ) m_thumbnail_small = m_thumbnail_path ;
if ( ! m_thumbnail_small . empty ( ) & & m_thumbnail_small . front ( ) = = ' / ' )
m_thumbnail_small . erase ( m_thumbnail_small . begin ( ) ) ;
if ( ! m_thumbnail_middle . empty ( ) & & m_thumbnail_middle . front ( ) = = ' / ' )
m_thumbnail_middle . erase ( m_thumbnail_middle . begin ( ) ) ;
m_model - > model_info - > metadata_items . emplace ( " Thumbnail " , m_thumbnail_middle ) ;
m_model - > model_info - > metadata_items . emplace ( " Poster " , m_thumbnail_middle ) ;
// we then loop again the entries to read other files stored in the archive
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive ) ;
mz_zip_archive_file_stat stat ;
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 ( ) , ' \\ ' , ' / ' ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " extract %1%th file %2%, total=%3% \n " ) % ( i + 1 ) % name % num_entries ;
if ( boost : : algorithm : : iequals ( name , QDS_PROJECT_CONFIG_FILE ) ) {
// extract slic3r print config file
ConfigSubstitutionContext config_substitutions ( ForwardCompatibilitySubstitutionRule : : Disable ) ;
_extract_project_config_from_archive ( archive , stat , config , config_substitutions , model ) ;
}
else if ( boost : : algorithm : : iequals ( name , QDS_MODEL_CONFIG_FILE ) ) {
// extract slic3r model config file
if ( ! _extract_xml_from_archive ( archive , stat , _handle_start_config_xml_element , _handle_end_config_xml_element ) ) {
add_error ( " Archive does not contain a valid model config " ) ;
return false ;
}
}
else if ( boost : : algorithm : : iequals ( name , SLICE_INFO_CONFIG_FILE ) ) {
m_parsing_slice_info = true ;
//extract slice info from archive
_extract_xml_from_archive ( archive , stat , _handle_start_config_xml_element , _handle_end_config_xml_element ) ;
m_parsing_slice_info = false ;
}
}
}
//QDS: load the plate info into plate_data_list
std : : map < int , PlateData * > : : iterator it = m_plater_data . begin ( ) ;
plate_data_list . clear ( ) ;
plate_data_list . reserve ( m_plater_data . size ( ) ) ;
for ( unsigned int i = 0 ; i < m_plater_data . size ( ) ; i + + )
{
PlateData * plate = new PlateData ( ) ;
plate_data_list . push_back ( plate ) ;
}
while ( it ! = m_plater_data . end ( ) )
{
if ( it - > first > m_plater_data . size ( ) )
{
add_error ( " invalid plate index " ) ;
return false ;
}
PlateData * plate = plate_data_list [ it - > first - 1 ] ;
plate - > locked = it - > second - > locked ;
plate - > plate_index = it - > second - > plate_index - 1 ;
plate - > obj_inst_map = it - > second - > obj_inst_map ;
plate - > gcode_file = it - > second - > gcode_file ;
plate - > gcode_prediction = it - > second - > gcode_prediction ;
plate - > gcode_weight = it - > second - > gcode_weight ;
plate - > toolpath_outside = it - > second - > toolpath_outside ;
plate - > is_support_used = it - > second - > is_support_used ;
plate - > is_label_object_enabled = it - > second - > is_label_object_enabled ;
plate - > skipped_objects = it - > second - > skipped_objects ;
plate - > slice_filaments_info = it - > second - > slice_filaments_info ;
plate - > printer_model_id = it - > second - > printer_model_id ;
plate - > nozzle_diameters = it - > second - > nozzle_diameters ;
plate - > warnings = it - > second - > warnings ;
plate - > thumbnail_file = it - > second - > thumbnail_file ;
if ( plate - > thumbnail_file . empty ( ) ) {
plate - > thumbnail_file = plate - > gcode_file ;
boost : : algorithm : : replace_all ( plate - > thumbnail_file , " .gcode " , " .png " ) ;
}
//plate->pattern_file = it->second->pattern_file;
plate - > no_light_thumbnail_file = it - > second - > no_light_thumbnail_file ;
plate - > top_file = it - > second - > top_file ;
plate - > pick_file = it - > second - > pick_file . empty ( ) ;
plate - > pattern_bbox_file = it - > second - > pattern_bbox_file . empty ( ) ;
plate - > config = it - > second - > config ;
if ( ! plate - > thumbnail_file . empty ( ) )
_extract_from_archive ( archive , plate - > thumbnail_file , [ & pixels = plate_data_list [ it - > first - 1 ] - > plate_thumbnail . pixels ] ( auto & archive , auto const & stat ) - > bool {
pixels . resize ( stat . m_uncomp_size ) ;
return mz_zip_reader_extract_to_mem ( & archive , stat . m_file_index , pixels . data ( ) , pixels . size ( ) , 0 ) ;
} ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , plate %1%, thumbnail_file=%2%, no_light_thumbnail_file=%3% " ) % it - > first % plate - > thumbnail_file % plate - > no_light_thumbnail_file ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , top_thumbnail_file=%1%, pick_thumbnail_file=%2% " ) % plate - > top_file % plate - > pick_file ;
it + + ;
}
lock . close ( ) ;
return true ;
}
void _QDS_3MF_Importer : : _destroy_xml_parser ( )
{
if ( m_xml_parser ! = nullptr ) {
XML_ParserFree ( m_xml_parser ) ;
m_xml_parser = nullptr ;
}
}
void _QDS_3MF_Importer : : _stop_xml_parser ( const std : : string & msg )
{
assert ( ! m_parse_error ) ;
assert ( m_parse_error_message . empty ( ) ) ;
assert ( m_xml_parser ! = nullptr ) ;
m_parse_error = true ;
m_parse_error_message = msg ;
XML_StopParser ( m_xml_parser , false ) ;
}
//QDS: add plate data related logic
bool _QDS_3MF_Importer : : _load_model_from_file (
std : : string filename ,
Model & model ,
PlateDataPtrs & plate_data_list ,
std : : vector < Preset * > & project_presets ,
DynamicPrintConfig & config ,
ConfigSubstitutionContext & config_substitutions ,
Import3mfProgressFn proFn ,
QDTProject * project ,
int plate_id )
{
bool cb_cancel = false ;
//QDS progress point
// prepare restore
if ( m_load_restore ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " import 3mf IMPORT_STAGE_RESTORE \n " ) ;
if ( proFn ) {
proFn ( IMPORT_STAGE_RESTORE , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " import 3mf IMPORT_STAGE_OPEN, m_load_restore=%1% \n " ) % m_load_restore ;
if ( proFn ) {
proFn ( IMPORT_STAGE_OPEN , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
struct close_lock
{
mz_zip_archive * archive ;
void close ( ) {
if ( archive ) {
close_zip_reader ( archive ) ;
archive = nullptr ;
}
}
~ close_lock ( ) {
close ( ) ;
}
} lock { & archive } ;
if ( ! open_zip_reader ( & archive , filename ) ) {
add_error ( " Unable to open the file " + filename ) ;
return false ;
}
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive ) ;
mz_zip_archive_file_stat stat ;
m_name = boost : : filesystem : : path ( filename ) . stem ( ) . string ( ) ;
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " import 3mf IMPORT_STAGE_READ_FILES \n " ) ;
if ( proFn ) {
proFn ( IMPORT_STAGE_READ_FILES , 0 , 3 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
// QDS: load relationships
if ( ! _extract_xml_from_archive ( archive , RELATIONSHIPS_FILE , _handle_start_relationships_element , _handle_end_relationships_element ) )
return false ;
if ( m_start_part_path . empty ( ) )
return false ;
// QDS: load sub models (Production Extension)
std : : string sub_rels = m_start_part_path ;
sub_rels . insert ( boost : : find_last ( sub_rels , " / " ) . end ( ) - sub_rels . begin ( ) , " _rels/ " ) ;
sub_rels . append ( " .rels " ) ;
if ( sub_rels . front ( ) = = ' / ' ) sub_rels = sub_rels . substr ( 1 ) ;
//check whether sub relation file is exist or not
int sub_index = mz_zip_reader_locate_file ( & archive , sub_rels . c_str ( ) , nullptr , 0 ) ;
if ( sub_index = = - 1 ) {
//no submodule files found, use only one 3dmodel.model
}
else {
_extract_xml_from_archive ( archive , sub_rels , _handle_start_relationships_element , _handle_end_relationships_element ) ;
int index = 0 ;
#if 0
for ( auto path : m_sub_model_paths ) {
if ( proFn ) {
proFn ( IMPORT_STAGE_READ_FILES , + + index , 3 + m_sub_model_paths . size ( ) , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
else
+ + index ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , read %1%th sub model file %2% \n " ) % index % path ;
m_sub_model_path = path ;
if ( ! _extract_from_archive ( archive , path , [ this ] ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) {
return _extract_model_from_archive ( archive , stat ) ;
} , m_load_restore ) ) {
add_error ( " Archive does not contain a valid model " ) ;
return false ;
}
m_sub_model_path . clear ( ) ;
}
# else
for ( auto path : m_sub_model_paths ) {
ObjectImporter * object_importer = new ObjectImporter ( this , filename , path ) ;
m_object_importers . push_back ( object_importer ) ;
}
bool object_load_result = true ;
boost : : mutex mutex ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , m_object_importers . size ( ) ) ,
[ this , & mutex , & object_load_result ] ( const tbb : : blocked_range < size_t > & importer_range ) {
CNumericLocalesSetter locales_setter ;
for ( size_t object_index = importer_range . begin ( ) ; object_index < importer_range . end ( ) ; + + object_index ) {
bool result = m_object_importers [ object_index ] - > extract_object_model ( ) ;
{
boost : : unique_lock l ( mutex ) ;
object_load_result & = result ;
}
}
}
) ;
if ( ! object_load_result ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , loading sub-objects error \n " ) ;
return false ;
}
//merge these objects into one
for ( auto obj_importer : m_object_importers ) {
for ( const IdToCurrentObjectMap : : value_type & obj : obj_importer - > object_list )
m_current_objects . insert ( { std : : move ( obj . first ) , std : : move ( obj . second ) } ) ;
for ( auto group_color : obj_importer - > object_group_id_to_color )
m_group_id_to_color . insert ( std : : move ( group_color ) ) ;
delete obj_importer ;
}
m_object_importers . clear ( ) ;
# endif
// QDS: load root model
if ( proFn ) {
proFn ( IMPORT_STAGE_READ_FILES , 2 , 3 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
}
//extract model files
if ( ! _extract_from_archive ( archive , m_start_part_path , [ this ] ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) {
return _extract_model_from_archive ( archive , stat ) ;
} ) ) {
add_error ( " Archive does not contain a valid model " ) ;
return false ;
}
if ( ! m_designer . empty ( ) ) {
m_model - > design_info = std : : make_shared < ModelDesignInfo > ( ) ;
m_model - > design_info - > DesignerId = m_designer_id ;
m_model - > design_info - > DesignerUserId = m_designer_user_id ;
m_model - > design_info - > Designer = m_designer ;
}
m_model - > profile_info = std : : make_shared < ModelProfileInfo > ( ) ;
m_model - > profile_info - > ProfileTile = m_profile_title ;
m_model - > profile_info - > ProfileCover = m_profile_cover ;
m_model - > profile_info - > ProfileDescription = m_Profile_description ;
m_model - > profile_info - > ProfileUserId = m_profile_user_id ;
m_model - > profile_info - > ProfileUserName = m_profile_user_name ;
m_model - > design_id = m_design_id ;
m_model - > model_info = std : : make_shared < ModelInfo > ( ) ;
m_model - > model_info - > load ( model_info ) ;
if ( ! m_thumbnail_small . empty ( ) ) m_model - > model_info - > metadata_items . emplace ( " Thumbnail_Small " , m_thumbnail_small ) ;
if ( ! m_thumbnail_middle . empty ( ) ) m_model - > model_info - > metadata_items . emplace ( " Thumbnail_Middle " , m_thumbnail_middle ) ;
//got project id
if ( project ) {
project - > project_model_id = m_model_id ;
project - > project_country_code = m_contry_code ;
}
//QDS: version check
bool dont_load_config = ! m_load_config ;
//w18
/*if (m_qidislicer_generator_version) {
Semver app_version = * ( Semver : : parse ( SLIC3R_VERSION ) ) ;
Semver file_version = * m_qidislicer_generator_version ;
2024-09-16 16:07:29 +08:00
//1.9.5
if ( file_version . maj ( ) > app_version . maj ( ) )
2024-09-03 09:34:33 +08:00
dont_load_config = true ;
}
else {
m_qidislicer_generator_version = Semver : : parse ( " 0.0.0.0 " ) ;
dont_load_config = true ;
} */
// 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 ) ) {
//QDS progress point
if ( proFn ) {
proFn ( IMPORT_STAGE_EXTRACT , i , num_entries , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
std : : string name ( stat . m_filename ) ;
std : : replace ( name . begin ( ) , name . end ( ) , ' \\ ' , ' / ' ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " extract %1%th file %2%, total=%3% " ) % ( i + 1 ) % name % num_entries ;
if ( name . find ( " /../ " ) ! = std : : string : : npos ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < boost : : format ( " , find file path including /../, not valid, skip it \n " ) ;
continue ;
}
if ( boost : : algorithm : : iequals ( name , QDS_LAYER_HEIGHTS_PROFILE_FILE ) ) {
// extract slic3r layer heights profile file
_extract_layer_heights_profile_config_from_archive ( archive , stat ) ;
}
else
if ( boost : : algorithm : : iequals ( name , LAYER_CONFIG_RANGES_FILE ) ) {
// extract slic3r layer config ranges file
_extract_layer_config_ranges_from_archive ( archive , stat , config_substitutions ) ;
}
//QDS: disable SLA related files currently
/*else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE)) {
// extract sla support points file
_extract_sla_support_points_from_archive ( archive , stat ) ;
}
else if ( boost : : algorithm : : iequals ( name , SLA_DRAIN_HOLES_FILE ) ) {
// extract sla support points file
_extract_sla_drain_holes_from_archive ( archive , stat ) ;
} */
//QDS: project setting file
//if (!dont_load_config && boost::algorithm::iequals(name, QDS_PRINT_CONFIG_FILE)) {
// extract slic3r print config file
// _extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
//} else
if ( ! dont_load_config & & boost : : algorithm : : iequals ( name , QDS_PROJECT_CONFIG_FILE ) ) {
// extract slic3r print config file
_extract_project_config_from_archive ( archive , stat , config , config_substitutions , model ) ;
}
else if ( boost : : algorithm : : iequals ( name , CUT_INFORMATION_FILE ) ) {
// extract object cut info
_extract_cut_information_from_archive ( archive , stat , config_substitutions ) ;
}
//QDS: project embedded presets
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , PROJECT_EMBEDDED_PRINT_PRESETS_FILE ) ) {
// extract slic3r layer config ranges file
_extract_project_embedded_presets_from_archive ( archive , stat , project_presets , model , Preset : : TYPE_PRINT , false ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , PROJECT_EMBEDDED_SLICE_PRESETS_FILE ) ) {
// extract slic3r layer config ranges file
_extract_project_embedded_presets_from_archive ( archive , stat , project_presets , model , Preset : : TYPE_PRINT ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , PROJECT_EMBEDDED_FILAMENT_PRESETS_FILE ) ) {
// extract slic3r layer config ranges file
_extract_project_embedded_presets_from_archive ( archive , stat , project_presets , model , Preset : : TYPE_FILAMENT ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , PROJECT_EMBEDDED_PRINTER_PRESETS_FILE ) ) {
// extract slic3r layer config ranges file
_extract_project_embedded_presets_from_archive ( archive , stat , project_presets , model , Preset : : TYPE_PRINTER ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : iequals ( name , CUSTOM_GCODE_PER_PRINT_Z_FILE ) ) {
// extract slic3r layer config ranges file
_extract_custom_gcode_per_print_z_from_archive ( archive , stat ) ;
}
else if ( boost : : algorithm : : iequals ( name , QDS_MODEL_CONFIG_FILE ) ) {
// extract slic3r model config file
if ( ! _extract_xml_from_archive ( archive , stat , _handle_start_config_xml_element , _handle_end_config_xml_element ) ) {
add_error ( " Archive does not contain a valid model config " ) ;
return false ;
}
}
else if ( ! dont_load_config & & boost : : algorithm : : iequals ( name , SLICE_INFO_CONFIG_FILE ) ) {
m_parsing_slice_info = true ;
//extract slice info from archive
_extract_xml_from_archive ( archive , stat , _handle_start_config_xml_element , _handle_end_config_xml_element ) ;
m_parsing_slice_info = false ;
}
else if ( boost : : algorithm : : istarts_with ( name , AUXILIARY_DIR ) ) {
// extract auxiliary directory to temp directory, do nothing for restore
if ( m_load_aux & & ! m_load_restore )
_extract_auxiliary_file_from_archive ( archive , stat , model ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , METADATA_DIR ) & & boost : : algorithm : : iends_with ( name , GCODE_EXTENSION ) ) {
//load gcode files
_extract_file_from_archive ( archive , stat ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , METADATA_DIR ) & & boost : : algorithm : : iends_with ( name , THUMBNAIL_EXTENSION ) ) {
//QDS parsing pattern thumbnail and plate thumbnails
_extract_file_from_archive ( archive , stat ) ;
}
else if ( ! dont_load_config & & boost : : algorithm : : istarts_with ( name , METADATA_DIR ) & & boost : : algorithm : : iends_with ( name , CALIBRATION_INFO_EXTENSION ) ) {
//QDS parsing pattern config files
_extract_file_from_archive ( archive , stat ) ;
}
else {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , %1% skipped, already parsed or a directory or not supported \n " ) % name ;
}
}
}
lock . close ( ) ;
if ( ! m_is_qdt_3mf ) {
// if the 3mf was not produced by QIDIStudio and there is more than one instance,
// split the object in as many objects as instances
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , found 3mf from other vendor, split as instance " ) ;
for ( const IdToModelObjectMap : : value_type & object : m_objects ) {
if ( object . second > = int ( m_model - > objects . size ( ) ) ) {
add_error ( " 3rd 3mf, invalid object, id: " + std : : to_string ( object . first . second ) ) ;
return false ;
}
ModelObject * model_object = m_model - > objects [ object . second ] ;
if ( model_object - > instances . size ( ) > 1 ) {
IdToCurrentObjectMap : : const_iterator current_object = m_current_objects . find ( object . first ) ;
if ( current_object = = m_current_objects . end ( ) ) {
add_error ( " 3rd 3mf, can not find object, id " + std : : to_string ( object . first . second ) ) ;
return false ;
}
std : : vector < Component > object_id_list ;
_generate_current_object_list ( object_id_list , object . first , m_current_objects ) ;
ObjectMetadata : : VolumeMetadataList volumes ;
ObjectMetadata : : VolumeMetadataList * volumes_ptr = nullptr ;
for ( int k = 0 ; k < object_id_list . size ( ) ; k + + )
{
Id object_id = object_id_list [ k ] . object_id ;
volumes . emplace_back ( object_id . second ) ;
}
// select as volumes
volumes_ptr = & volumes ;
// for each instance after the 1st, create a new model object containing only that instance
// and copy into it the geometry
while ( model_object - > instances . size ( ) > 1 ) {
ModelObject * new_model_object = m_model - > add_object ( * model_object ) ;
new_model_object - > clear_instances ( ) ;
new_model_object - > add_instance ( * model_object - > instances . back ( ) ) ;
model_object - > delete_last_instance ( ) ;
if ( ! _generate_volumes_new ( * new_model_object , object_id_list , * volumes_ptr , config_substitutions ) )
return false ;
}
}
}
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , process group colors, size %1% \n " ) % m_group_id_to_color . size ( ) ;
std : : map < int , int > color_group_id_to_extruder_id_map ;
std : : map < std : : string , int > color_to_extruder_id_map ;
int extruder_id = 0 ;
for ( auto group_iter = m_group_id_to_color . begin ( ) ; group_iter ! = m_group_id_to_color . end ( ) ; + + group_iter ) {
auto color_iter = color_to_extruder_id_map . find ( group_iter - > second ) ;
if ( color_iter = = color_to_extruder_id_map . end ( ) ) {
+ + extruder_id ;
color_to_extruder_id_map [ group_iter - > second ] = extruder_id ;
color_group_id_to_extruder_id_map [ group_iter - > first ] = extruder_id ;
} else {
color_group_id_to_extruder_id_map [ group_iter - > first ] = color_iter - > second ;
}
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , begin to assemble objects, size %1% \n " ) % m_objects . size ( ) ;
//only load objects in plate_id
PlateData * current_plate_data = nullptr ;
if ( ( plate_id > 0 ) & & ( plate_id < = m_plater_data . size ( ) ) ) {
std : : map < int , PlateData * > : : iterator it = m_plater_data . find ( plate_id ) ;
if ( it ! = m_plater_data . end ( ) ) {
current_plate_data = it - > second ;
}
}
for ( const IdToModelObjectMap : : value_type & object : m_objects ) {
if ( object . second > = int ( m_model - > objects . size ( ) ) ) {
add_error ( " invalid object, id: " + std : : to_string ( object . first . second ) ) ;
return false ;
}
if ( current_plate_data ) {
std : : map < int , std : : pair < int , int > > : : iterator it = current_plate_data - > obj_inst_map . find ( object . first . second ) ;
if ( it = = current_plate_data - > obj_inst_map . end ( ) ) {
//not in current plate, skip
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , could not find object %1% in plate %2%, skip it \n " ) % object . first . second % plate_id ;
continue ;
}
}
ModelObject * model_object = m_model - > objects [ object . second ] ;
/*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 ;
} */
// 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 ) ;
if ( obj_layer_heights_profile ! = m_layer_heights_profiles . end ( ) )
model_object - > layer_height_profile . set ( std : : move ( obj_layer_heights_profile - > second ) ) ;
// 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 = std : : move ( obj_layer_config_ranges - > second ) ;
// 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);
if ( obj_sla_support_points ! = m_sla_support_points . end ( ) & & ! obj_sla_support_points - > second . empty ( ) ) {
model_object - > sla_support_points = std : : move ( obj_sla_support_points - > second ) ;
model_object - > sla_points_status = sla : : PointsStatus : : UserModified ;
}
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 = std : : move ( obj_drain_holes - > second ) ;
} */
std : : vector < Component > object_id_list ;
_generate_current_object_list ( object_id_list , object . first , m_current_objects ) ;
ObjectMetadata : : VolumeMetadataList volumes ;
ObjectMetadata : : VolumeMetadataList * volumes_ptr = nullptr ;
IdToMetadataMap : : iterator obj_metadata = m_objects_metadata . find ( object . first . second ) ;
if ( obj_metadata ! = m_objects_metadata . end ( ) ) {
// config data has been found, this model was saved using slic3r pe
// apply object's name and config data
for ( const Metadata & metadata : obj_metadata - > second . metadata ) {
if ( metadata . key = = " name " )
model_object - > name = metadata . value ;
//QDS: add module name
else if ( metadata . key = = " module " )
model_object - > module_name = metadata . value ;
else
model_object - > config . set_deserialize ( metadata . key , metadata . value , config_substitutions ) ;
}
// 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() - 1);
for ( int k = 0 ; k < object_id_list . size ( ) ; k + + )
{
Id object_id = object_id_list [ k ] . object_id ;
volumes . emplace_back ( object_id . second ) ;
}
IdToCurrentObjectMap : : const_iterator current_object = m_current_objects . find ( object . first ) ;
if ( current_object ! = m_current_objects . end ( ) ) {
// get name
if ( ! current_object - > second . name . empty ( ) )
model_object - > name = current_object - > second . name ;
else
model_object - > name = " Object_ " + std : : to_string ( object . second + 1 ) ;
// get color
auto extruder_itor = color_group_id_to_extruder_id_map . find ( current_object - > second . pid ) ;
if ( extruder_itor ! = color_group_id_to_extruder_id_map . end ( ) ) {
model_object - > config . set_key_value ( " extruder " , new ConfigOptionInt ( extruder_itor - > second ) ) ;
}
}
// select as volumes
volumes_ptr = & volumes ;
}
if ( ! _generate_volumes_new ( * model_object , object_id_list , * volumes_ptr , config_substitutions ) )
return false ;
// Apply cut information for object if any was loaded
// m_cut_object_ids are indexed by a 1 based model object index.
IdToCutObjectInfoMap : : iterator cut_object_info = m_cut_object_infos . find ( object . second + 1 ) ;
if ( cut_object_info ! = m_cut_object_infos . end ( ) ) {
model_object - > cut_id = cut_object_info - > second . id ;
for ( auto connector : cut_object_info - > second . connectors ) {
assert ( 0 < = connector . volume_id & & connector . volume_id < = int ( model_object - > volumes . size ( ) ) ) ;
model_object - > volumes [ connector . volume_id ] - > cut_info =
ModelVolume : : CutInfo ( CutConnectorType ( connector . type ) , connector . radius , connector . height , connector . r_tolerance , connector . h_tolerance , true ) ;
}
}
}
// If instances contain a single volume, the volume offset should be 0,0,0
// This equals to say that instance world position and volume world position should match
// Correct all instances/volumes for which this does not hold
for ( int obj_id = 0 ; obj_id < int ( model . objects . size ( ) ) ; + + obj_id ) {
ModelObject * o = model . objects [ obj_id ] ;
if ( o - > volumes . size ( ) = = 1 ) {
ModelVolume * v = o - > volumes . front ( ) ;
const Slic3r : : Geometry : : Transformation & first_inst_trafo = o - > instances . front ( ) - > get_transformation ( ) ;
const Vec3d world_vol_offset = ( first_inst_trafo * v - > get_transformation ( ) ) . get_offset ( ) ;
const Vec3d world_inst_offset = first_inst_trafo . get_offset ( ) ;
if ( ! world_vol_offset . isApprox ( world_inst_offset ) ) {
const Slic3r : : Geometry : : Transformation & vol_trafo = v - > get_transformation ( ) ;
for ( int inst_id = 0 ; inst_id < int ( o - > instances . size ( ) ) ; + + inst_id ) {
ModelInstance * i = o - > instances [ inst_id ] ;
const Slic3r : : Geometry : : Transformation & inst_trafo = i - > get_transformation ( ) ;
i - > set_offset ( ( inst_trafo * vol_trafo ) . get_offset ( ) ) ;
}
v - > set_offset ( Vec3d : : Zero ( ) ) ;
}
}
}
int object_idx = 0 ;
for ( ModelObject * o : model . objects ) {
int volume_idx = 0 ;
for ( ModelVolume * v : o - > volumes ) {
if ( v - > source . input_file . empty ( ) & & v - > type ( ) = = ModelVolumeType : : MODEL_PART ) {
v - > source . input_file = filename ;
if ( v - > source . volume_idx = = - 1 )
v - > source . volume_idx = volume_idx ;
if ( v - > source . object_idx = = - 1 )
v - > source . object_idx = object_idx ;
}
+ + volume_idx ;
}
+ + object_idx ;
}
const ConfigOptionStrings * filament_ids_opt = config . option < ConfigOptionStrings > ( " filament_settings_id " ) ;
int max_filament_id = filament_ids_opt ? filament_ids_opt - > size ( ) : std : : numeric_limits < int > : : max ( ) ;
for ( ModelObject * mo : m_model - > objects ) {
const ConfigOptionInt * extruder_opt = dynamic_cast < const ConfigOptionInt * > ( mo - > config . option ( " extruder " ) ) ;
int extruder_id = 0 ;
if ( extruder_opt ! = nullptr )
extruder_id = extruder_opt - > getInt ( ) ;
if ( extruder_id = = 0 | | extruder_id > max_filament_id )
mo - > config . set_key_value ( " extruder " , new ConfigOptionInt ( 1 ) ) ;
if ( mo - > volumes . size ( ) = = 1 ) {
mo - > volumes [ 0 ] - > config . erase ( " extruder " ) ;
}
else {
for ( ModelVolume * mv : mo - > volumes ) {
const ConfigOptionInt * vol_extruder_opt = dynamic_cast < const ConfigOptionInt * > ( mv - > config . option ( " extruder " ) ) ;
if ( vol_extruder_opt = = nullptr )
continue ;
if ( vol_extruder_opt - > getInt ( ) = = 0 )
mv - > config . erase ( " extruder " ) ;
else if ( vol_extruder_opt - > getInt ( ) > max_filament_id )
mv - > config . set_key_value ( " extruder " , new ConfigOptionInt ( 1 ) ) ;
}
}
}
// // fixes the min z of the model if negative
// model.adjust_min_z();
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " import 3mf IMPORT_STAGE_LOADING_PLATES, m_plater_data size %1%, m_backup_path %2% \n " ) % m_plater_data . size ( ) % m_backup_path ;
if ( proFn ) {
proFn ( IMPORT_STAGE_LOADING_PLATES , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
//QDS: load the plate info into plate_data_list
std : : map < int , PlateData * > : : iterator it = m_plater_data . begin ( ) ;
plate_data_list . clear ( ) ;
plate_data_list . reserve ( m_plater_data . size ( ) ) ;
for ( unsigned int i = 0 ; i < m_plater_data . size ( ) ; i + + )
{
PlateData * plate = new PlateData ( ) ;
plate_data_list . push_back ( plate ) ;
}
while ( it ! = m_plater_data . end ( ) )
{
if ( it - > first > m_plater_data . size ( ) )
{
add_error ( " invalid plate index " ) ;
return false ;
}
plate_data_list [ it - > first - 1 ] - > locked = it - > second - > locked ;
plate_data_list [ it - > first - 1 ] - > plate_index = it - > second - > plate_index - 1 ;
plate_data_list [ it - > first - 1 ] - > plate_name = it - > second - > plate_name ;
plate_data_list [ it - > first - 1 ] - > obj_inst_map = it - > second - > obj_inst_map ;
plate_data_list [ it - > first - 1 ] - > gcode_file = ( m_load_restore | | it - > second - > gcode_file . empty ( ) ) ? it - > second - > gcode_file : m_backup_path + " / " + it - > second - > gcode_file ;
plate_data_list [ it - > first - 1 ] - > gcode_prediction = it - > second - > gcode_prediction ;
plate_data_list [ it - > first - 1 ] - > gcode_weight = it - > second - > gcode_weight ;
plate_data_list [ it - > first - 1 ] - > toolpath_outside = it - > second - > toolpath_outside ;
plate_data_list [ it - > first - 1 ] - > is_support_used = it - > second - > is_support_used ;
plate_data_list [ it - > first - 1 ] - > is_label_object_enabled = it - > second - > is_label_object_enabled ;
plate_data_list [ it - > first - 1 ] - > slice_filaments_info = it - > second - > slice_filaments_info ;
plate_data_list [ it - > first - 1 ] - > skipped_objects = it - > second - > skipped_objects ;
plate_data_list [ it - > first - 1 ] - > warnings = it - > second - > warnings ;
plate_data_list [ it - > first - 1 ] - > thumbnail_file = ( m_load_restore | | it - > second - > thumbnail_file . empty ( ) ) ? it - > second - > thumbnail_file : m_backup_path + " / " + it - > second - > thumbnail_file ;
//plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file;
plate_data_list [ it - > first - 1 ] - > no_light_thumbnail_file = ( m_load_restore | | it - > second - > no_light_thumbnail_file . empty ( ) ) ? it - > second - > no_light_thumbnail_file : m_backup_path + " / " + it - > second - > no_light_thumbnail_file ;
plate_data_list [ it - > first - 1 ] - > top_file = ( m_load_restore | | it - > second - > top_file . empty ( ) ) ? it - > second - > top_file : m_backup_path + " / " + it - > second - > top_file ;
plate_data_list [ it - > first - 1 ] - > pick_file = ( m_load_restore | | it - > second - > pick_file . empty ( ) ) ? it - > second - > pick_file : m_backup_path + " / " + it - > second - > pick_file ;
plate_data_list [ it - > first - 1 ] - > pattern_bbox_file = ( m_load_restore | | it - > second - > pattern_bbox_file . empty ( ) ) ? it - > second - > pattern_bbox_file : m_backup_path + " / " + it - > second - > pattern_bbox_file ;
plate_data_list [ it - > first - 1 ] - > config = it - > second - > config ;
current_plate_data = plate_data_list [ it - > first - 1 ] ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , plate %1%, thumbnail_file=%2%, no_light_thumbnail_file=%3% " ) % it - > first % plate_data_list [ it - > first - 1 ] - > thumbnail_file % plate_data_list [ it - > first - 1 ] - > no_light_thumbnail_file ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , top_thumbnail_file=%1%, pick_thumbnail_file=%2% " ) % plate_data_list [ it - > first - 1 ] - > top_file % plate_data_list [ it - > first - 1 ] - > pick_file ;
it + + ;
//update the arrange order
std : : map < int , std : : pair < int , int > > : : iterator map_it = current_plate_data - > obj_inst_map . begin ( ) ;
while ( map_it ! = current_plate_data - > obj_inst_map . end ( ) ) {
int obj_index , obj_id = map_it - > first , inst_index = map_it - > second . first ;
IndexToPathMap : : iterator index_iter = m_index_paths . find ( obj_id ) ;
if ( index_iter = = m_index_paths . end ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__
< < boost : : format ( " , can not find object from plate's obj_map, id=%1%, skip this object " ) % obj_id ;
map_it + + ;
continue ;
}
Id temp_id = std : : make_pair ( index_iter - > second , index_iter - > first ) ;
IdToModelObjectMap : : iterator object_item = m_objects . find ( temp_id ) ;
if ( object_item = = m_objects . end ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__
< < boost : : format ( " , can not find object from plate's obj_map, ID <%1%, %2%>, skip this object " ) % index_iter - > second % index_iter - > first ;
map_it + + ;
continue ;
}
obj_index = object_item - > second ;
if ( obj_index > = m_model - > objects . size ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " invalid object id %1% \n " ) % obj_index ;
map_it + + ;
continue ;
}
ModelObject * obj = m_model - > objects [ obj_index ] ;
if ( inst_index > = obj - > instances . size ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " invalid instance id %1% \n " ) % inst_index ;
map_it + + ;
continue ;
}
ModelInstance * inst = obj - > instances [ inst_index ] ;
inst - > loaded_id = map_it - > second . second ;
map_it + + ;
}
}
if ( ( plate_id > 0 ) & & ( plate_id < = m_plater_data . size ( ) ) ) {
//remove the no need objects
std : : vector < size_t > delete_ids ;
for ( int index = 0 ; index < m_model - > objects . size ( ) ; index + + ) {
ModelObject * obj = m_model - > objects [ index ] ;
if ( obj - > volumes . size ( ) = = 0 ) {
//remove this model objects
delete_ids . push_back ( index ) ;
}
}
for ( int index = delete_ids . size ( ) - 1 ; index > = 0 ; index - - )
m_model - > delete_object ( delete_ids [ index ] ) ;
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " import 3mf IMPORT_STAGE_FINISH \n " ) ;
if ( proFn ) {
proFn ( IMPORT_STAGE_FINISH , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
return true ;
}
bool _QDS_3MF_Importer : : _extract_from_archive ( mz_zip_archive & archive , std : : string const & path , std : : function < bool ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) > extract , bool restore )
{
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive ) ;
mz_zip_archive_file_stat stat ;
std : : string path2 = path ;
if ( path2 . front ( ) = = ' / ' ) path2 = path2 . substr ( 1 ) ;
// try utf8 encoding
int index = mz_zip_reader_locate_file ( & archive , path2 . c_str ( ) , nullptr , 0 ) ;
if ( index < 0 ) {
// try native encoding
std : : string native_path = encode_path ( path2 . c_str ( ) ) ;
index = mz_zip_reader_locate_file ( & archive , native_path . c_str ( ) , nullptr , 0 ) ;
}
if ( index < 0 ) {
// try unicode path extra
std : : string extra ( 1024 , 0 ) ;
for ( mz_uint i = 0 ; i < archive . m_total_files ; + + i ) {
size_t n = mz_zip_reader_get_extra ( & archive , i , extra . data ( ) , extra . size ( ) ) ;
if ( n > 0 & & path2 = = ZipUnicodePathExtraField : : decode ( extra . substr ( 0 , n ) ) ) {
index = i ;
break ;
}
}
}
if ( index < 0 | | ! mz_zip_reader_file_stat ( & archive , index , & stat ) ) {
if ( restore ) {
std : : vector < std : : string > paths = { m_backup_path + path } ;
if ( ! m_origin_file . empty ( ) ) paths . push_back ( m_origin_file ) ;
for ( auto & path2 : paths ) {
bool result = false ;
if ( boost : : filesystem : : exists ( path2 ) ) {
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
if ( open_zip_reader ( & archive , path2 ) ) {
result = _extract_from_archive ( archive , path , extract ) ;
close_zip_reader ( & archive ) ;
}
}
if ( result ) return result ;
}
}
char error_buf [ 1024 ] ;
: : snprintf ( error_buf , 1024 , " File %s not found from archive " , path . c_str ( ) ) ;
add_error ( error_buf ) ;
return false ;
}
try
{
if ( ! extract ( archive , stat ) ) {
return false ;
}
}
catch ( const std : : exception & e )
{
// ensure the zip archive is closed and rethrow the exception
add_error ( e . what ( ) ) ;
return false ;
}
return true ;
}
bool _QDS_3MF_Importer : : _extract_xml_from_archive ( mz_zip_archive & archive , const std : : string & path , XML_StartElementHandler start_handler , XML_EndElementHandler end_handler )
{
return _extract_from_archive ( archive , path , [ this , start_handler , end_handler ] ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat ) {
return _extract_xml_from_archive ( archive , stat , start_handler , end_handler ) ;
} ) ;
}
bool _QDS_3MF_Importer : : _extract_xml_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , XML_StartElementHandler start_handler , XML_EndElementHandler end_handler )
{
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 , start_handler , end_handler ) ;
XML_SetCharacterDataHandler ( m_xml_parser , _QDS_3MF_Importer : : _handle_xml_characters ) ;
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 ] ;
: : snprintf ( error_buf , 1024 , " Error (%s) while parsing xml file at line %d " , XML_ErrorString ( XML_GetErrorCode ( m_xml_parser ) ) , ( int ) XML_GetCurrentLineNumber ( m_xml_parser ) ) ;
add_error ( error_buf ) ;
return false ;
}
return true ;
}
bool _QDS_3MF_Importer : : _extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
{
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 , _QDS_3MF_Importer : : _handle_start_model_xml_element , _QDS_3MF_Importer : : _handle_end_model_xml_element ) ;
XML_SetCharacterDataHandler ( m_xml_parser , _QDS_3MF_Importer : : _handle_xml_characters ) ;
struct CallbackData
{
XML_Parser & parser ;
_QDS_3MF_Importer & importer ;
const mz_zip_archive_file_stat & stat ;
CallbackData ( XML_Parser & parser , _QDS_3MF_Importer & importer , const mz_zip_archive_file_stat & stat ) : parser ( parser ) , importer ( importer ) , stat ( stat ) { }
} ;
CallbackData data ( m_xml_parser , * this , stat ) ;
mz_bool res = 0 ;
try
{
mz_file_write_func callback = [ ] ( 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 ) | | data - > importer . parse_error ( ) ) {
char error_buf [ 1024 ] ;
: : snprintf ( error_buf , 1024 , " Error (%s) while parsing '%s' at line %d " , data - > importer . parse_error_message ( ) , data - > stat . m_filename , ( int ) XML_GetCurrentLineNumber ( data - > parser ) ) ;
throw Slic3r : : FileIOError ( error_buf ) ;
}
return n ;
} ;
void * opaque = & data ;
res = mz_zip_reader_extract_to_callback ( & archive , stat . m_file_index , callback , opaque , 0 ) ;
}
catch ( const version_error & e )
{
// rethrow the exception
throw Slic3r : : FileIOError ( e . what ( ) ) ;
}
catch ( std : : exception & e )
{
add_error ( e . what ( ) ) ;
return false ;
}
if ( res = = 0 ) {
add_error ( " Error while extracting model data from zip archive " ) ;
return false ;
}
return true ;
}
void _QDS_3MF_Importer : : _extract_cut_information_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , ConfigSubstitutionContext & config_substitutions )
{
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 cut information data to buffer " ) ;
return ;
}
std : : istringstream iss ( buffer ) ; // wrap returned xml to istringstream
pt : : ptree objects_tree ;
pt : : read_xml ( iss , objects_tree ) ;
for ( const auto & object : objects_tree . get_child ( " objects " ) ) {
pt : : ptree object_tree = object . second ;
int obj_idx = object_tree . get < int > ( " <xmlattr>.id " , - 1 ) ;
if ( obj_idx < = 0 ) {
add_error ( " Found invalid object id " ) ;
continue ;
}
IdToCutObjectInfoMap : : iterator object_item = m_cut_object_infos . find ( obj_idx ) ;
if ( object_item ! = m_cut_object_infos . end ( ) ) {
add_error ( " Found duplicated cut_object_id " ) ;
continue ;
}
CutObjectBase cut_id ;
std : : vector < CutObjectInfo : : Connector > connectors ;
for ( const auto & obj_cut_info : object_tree ) {
if ( obj_cut_info . first = = " cut_id " ) {
pt : : ptree cut_id_tree = obj_cut_info . second ;
cut_id = CutObjectBase ( ObjectID ( cut_id_tree . get < size_t > ( " <xmlattr>.id " ) ) , cut_id_tree . get < size_t > ( " <xmlattr>.check_sum " ) ,
cut_id_tree . get < size_t > ( " <xmlattr>.connectors_cnt " ) ) ;
}
if ( obj_cut_info . first = = " connectors " ) {
pt : : ptree cut_connectors_tree = obj_cut_info . second ;
for ( const auto & cut_connector : cut_connectors_tree ) {
if ( cut_connector . first ! = " connector " ) continue ;
pt : : ptree connector_tree = cut_connector . second ;
CutObjectInfo : : Connector connector = { connector_tree . get < int > ( " <xmlattr>.volume_id " ) , connector_tree . get < int > ( " <xmlattr>.type " ) ,
connector_tree . get < float > ( " <xmlattr>.radius " , 0.f ) , connector_tree . get < float > ( " <xmlattr>.height " , 0.f ) ,
connector_tree . get < float > ( " <xmlattr>.r_tolerance " ) , connector_tree . get < float > ( " <xmlattr>.h_tolerance " ) } ;
connectors . emplace_back ( connector ) ;
}
}
}
CutObjectInfo cut_info { cut_id , connectors } ;
m_cut_object_infos . insert ( { obj_idx , cut_info } ) ;
}
}
}
void _QDS_3MF_Importer : : _extract_print_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig & config , ConfigSubstitutionContext & config_substitutions , const std : : string & archive_filename )
{
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 config data to buffer " ) ;
return ;
}
ConfigBase : : load_from_gcode_string_legacy ( config , buffer . data ( ) , config_substitutions ) ;
}
}
//QDS: extract project config from json files
void _QDS_3MF_Importer : : _extract_project_config_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig & config , ConfigSubstitutionContext & config_substitutions , Model & model )
{
if ( stat . m_uncomp_size > 0 ) {
const std : : string & temp_path = model . get_backup_path ( ) ;
std : : string dest_file = temp_path + std : : string ( " / " ) + " _temp_3.config " ; ;
std : : string dest_zip_file = encode_path ( dest_file . c_str ( ) ) ;
mz_bool res = mz_zip_reader_extract_to_file ( & archive , stat . m_file_index , dest_zip_file . c_str ( ) , 0 ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , extract %1% from 3mf %2%, ret %3% \n " ) % dest_file % stat . m_filename % res ;
if ( res = = 0 ) {
add_error ( " Error while extract project config file to file " ) ;
return ;
}
std : : map < std : : string , std : : string > key_values ;
std : : string reason ;
int ret = config . load_from_json ( dest_file , config_substitutions , true , key_values , reason ) ;
if ( ret ) {
add_error ( " Error load config from json: " + reason ) ;
return ;
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , load project config file successfully from %1% \n " ) % dest_file ;
}
}
//QDS: extract project embedded presets
void _QDS_3MF_Importer : : _extract_project_embedded_presets_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , std : : vector < Preset * > & project_presets , Model & model , Preset : : Type type , bool use_json )
{
if ( stat . m_uncomp_size > 0 ) {
/*std::string src_file = decode_path(stat.m_filename);
std : : size_t found = src_file . find ( METADATA_DIR ) ;
if ( found ! = std : : string : : npos )
src_file = src_file . substr ( found + METADATA_STR_LEN ) ;
else
return ; */
std : : string dest_file = m_backup_path + std : : string ( " / " ) + " _temp_2.config " ; ;
std : : string dest_zip_file = encode_path ( dest_file . c_str ( ) ) ;
mz_bool res = mz_zip_reader_extract_to_file ( & archive , stat . m_file_index , dest_zip_file . c_str ( ) , 0 ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , extract %1% from 3mf %2%, ret %3% \n " ) % dest_file % stat . m_filename % res ;
if ( res = = 0 ) {
add_error ( " Error while extract auxiliary file to file " ) ;
return ;
}
//load presets
DynamicPrintConfig config ;
//ConfigSubstitutions config_substitutions = config.load_from_ini(dest_file, Enable);
std : : map < std : : string , std : : string > key_values ;
std : : string reason ;
ConfigSubstitutions config_substitutions = use_json ? config . load_from_json ( dest_file , Enable , key_values , reason ) : config . load_from_ini ( dest_file , Enable ) ;
if ( ! reason . empty ( ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " , load project embedded config from %1% failed \n " ) % dest_file ;
//skip this file
return ;
}
ConfigOptionString * print_name ;
ConfigOptionStrings * filament_names ;
std : : string preset_name ;
if ( type = = Preset : : TYPE_PRINT ) {
print_name = dynamic_cast < ConfigOptionString * > ( config . option ( " print_settings_id " ) ) ;
if ( ! print_name ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " , can not found print_settings_id from %1% \n " ) % dest_file ;
//skip this file
return ;
}
preset_name = print_name - > value ;
}
else if ( type = = Preset : : TYPE_FILAMENT ) {
filament_names = dynamic_cast < ConfigOptionStrings * > ( config . option ( " filament_settings_id " ) ) ;
if ( ! filament_names ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " , can not found filament_settings_id from %1% \n " ) % dest_file ;
//skip this file
return ;
}
preset_name = filament_names - > values [ 0 ] ;
}
else if ( type = = Preset : : TYPE_PRINTER ) {
print_name = dynamic_cast < ConfigOptionString * > ( config . option ( " printer_settings_id " ) ) ;
if ( ! print_name ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " , can not found printer_settings_id from %1% \n " ) % dest_file ;
//skip this file
return ;
}
preset_name = print_name - > value ;
}
else {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " , invalid type %1% from file %2% \n " ) % Preset : : get_type_string ( type ) % dest_file ;
//skip this file
return ;
}
Preset * preset = new Preset ( type , preset_name , false ) ;
preset - > file = dest_file ;
preset - > config = std : : move ( config ) ;
preset - > loaded = true ;
preset - > is_project_embedded = true ;
preset - > is_external = true ;
preset - > is_dirty = false ;
std : : string version_str = key_values [ QDT_JSON_KEY_VERSION ] ;
boost : : optional < Semver > version = Semver : : parse ( version_str ) ;
if ( version ) {
preset - > version = * version ;
}
else
preset - > version = this - > m_qidislicer_generator_version ? * this - > m_qidislicer_generator_version : Semver ( ) ;
/*for (int i = 0; i < config_substitutions.size(); i++)
{
//ConfigSubstitution config_substitution;
//config_substitution.opt_def = optdef;
//config_substitution.old_value = value;
//config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
preset - > loading_substitutions . emplace_back ( std : : move ( config_substitutions [ i ] ) ) ;
} */
if ( ! config_substitutions . empty ( ) ) {
preset - > loading_substitutions = new ConfigSubstitutions ( ) ;
* ( preset - > loading_substitutions ) = std : : move ( config_substitutions ) ;
}
project_presets . push_back ( preset ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , create one project embedded preset: %1% from %2%, type %3% \n " ) % preset_name % dest_file % Preset : : get_type_string ( type ) ;
}
}
void _QDS_3MF_Importer : : _extract_auxiliary_file_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , Model & model )
{
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , stat.m_uncomp_size is %1% " ) % stat . m_uncomp_size ;
if ( stat . m_uncomp_size > 0 ) {
std : : string dest_file ;
if ( stat . m_is_utf8 ) {
dest_file = stat . m_filename ;
} else {
std : : string extra ( 1024 , 0 ) ;
size_t n = mz_zip_reader_get_extra ( & archive , stat . m_file_index , extra . data ( ) , extra . size ( ) ) ;
dest_file = ZipUnicodePathExtraField : : decode ( extra . substr ( 0 , n ) , stat . m_filename ) ;
}
std : : string temp_path = model . get_auxiliary_file_temp_path ( ) ;
//aux directory from model
boost : : filesystem : : path dir = boost : : filesystem : : path ( temp_path ) ;
std : : size_t found = dest_file . find ( AUXILIARY_DIR ) ;
if ( found ! = std : : string : : npos )
dest_file = dest_file . substr ( found + AUXILIARY_STR_LEN ) ;
else
return ;
if ( dest_file . find ( ' / ' ) ! = std : : string : : npos ) {
boost : : filesystem : : path src_path = boost : : filesystem : : path ( dest_file ) ;
boost : : filesystem : : path parent_path = src_path . parent_path ( ) ;
std : : string temp_path = dir . string ( ) + std : : string ( " / " ) + parent_path . string ( ) ;
boost : : filesystem : : path parent_full_path = boost : : filesystem : : path ( temp_path ) ;
if ( ! boost : : filesystem : : exists ( parent_full_path ) )
boost : : filesystem : : create_directories ( parent_full_path ) ;
}
dest_file = dir . string ( ) + std : : string ( " / " ) + dest_file ;
std : : string dest_zip_file = encode_path ( dest_file . c_str ( ) ) ;
mz_bool res = mz_zip_reader_extract_to_file ( & archive , stat . m_file_index , dest_zip_file . c_str ( ) , 0 ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , extract %1% from 3mf %2%, ret %3% \n " ) % dest_file % stat . m_filename % res ;
if ( res = = 0 ) {
add_error ( " Error while extract auxiliary file to file " ) ;
return ;
}
}
}
void _QDS_3MF_Importer : : _extract_file_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
{
if ( stat . m_uncomp_size > 0 ) {
std : : string src_file = decode_path ( stat . m_filename ) ;
// QDS: use backup path
//aux directory from model
boost : : filesystem : : path dest_path = boost : : filesystem : : path ( m_backup_path + " / " + src_file ) ;
std : : string dest_zip_file = encode_path ( dest_path . string ( ) . c_str ( ) ) ;
mz_bool res = mz_zip_reader_extract_to_file ( & archive , stat . m_file_index , dest_zip_file . c_str ( ) , 0 ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " , extract %1% from 3mf %2%, ret %3% \n " ) % dest_path % stat . m_filename % res ;
if ( res = = 0 ) {
add_error ( " Error while extract file to temp directory " ) ;
return ;
}
}
return ;
}
void _QDS_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_to_mem ( & archive , stat . m_file_index , ( 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 ( { object_id , profile } ) ;
}
}
}
void _QDS_3MF_Importer : : _extract_layer_config_ranges_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , ConfigSubstitutionContext & config_substitutions )
{
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 ;
}
std : : istringstream iss ( buffer ) ; // wrap returned xml to istringstream
pt : : ptree objects_tree ;
pt : : read_xml ( iss , objects_tree ) ;
for ( const auto & object : objects_tree . get_child ( " objects " ) ) {
pt : : ptree object_tree = object . second ;
int obj_idx = object_tree . get < int > ( " <xmlattr>.id " , - 1 ) ;
if ( obj_idx < = 0 ) {
add_error ( " Found invalid object id " ) ;
continue ;
}
IdToLayerConfigRangesMap : : iterator object_item = m_layer_config_ranges . find ( obj_idx ) ;
if ( object_item ! = m_layer_config_ranges . end ( ) ) {
add_error ( " Found duplicated layer config range " ) ;
continue ;
}
t_layer_config_ranges config_ranges ;
for ( const auto & range : object_tree ) {
if ( range . first ! = " range " )
continue ;
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 " ) ;
// get Z range information
DynamicPrintConfig config ;
for ( const auto & option : range_tree ) {
if ( option . first ! = " option " )
continue ;
std : : string opt_key = option . second . get < std : : string > ( " <xmlattr>.opt_key " ) ;
std : : string value = option . second . data ( ) ;
config . set_deserialize ( opt_key , value , config_substitutions ) ;
}
config_ranges [ { min_z , max_z } ] . assign_config ( std : : move ( config ) ) ;
}
if ( ! config_ranges . empty ( ) )
m_layer_config_ranges . insert ( { obj_idx , std : : move ( config_ranges ) } ) ;
}
}
}
/*
void _QDS_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 ) ;
// Info on format versioning - see 3mf.hpp
int version = 0 ;
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
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 ;
}
IdToSlaSupportPointsMap : : iterator object_item = m_sla_support_points . find ( object_id ) ;
if ( object_item ! = m_sla_support_points . end ( ) ) {
add_error ( " Found duplicated SLA support points " ) ;
continue ;
}
std : : vector < std : : string > object_data_points ;
boost : : split ( object_data_points , object_data [ 1 ] , boost : : is_any_of ( " " ) , boost : : token_compress_off ) ;
std : : vector < sla : : SupportPoint > sla_support_points ;
if ( version = = 0 ) {
for ( unsigned int i = 0 ; i < object_data_points . size ( ) ; i + = 3 )
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 ( ) ) ) ,
0.4f ,
false ) ;
}
if ( version = = 1 ) {
for ( unsigned int i = 0 ; i < object_data_points . size ( ) ; i + = 5 )
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 ) ;
}
if ( ! sla_support_points . empty ( ) )
m_sla_support_points . insert ( { object_id , sla_support_points } ) ;
}
}
}
void _QDS_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 ( ) ) ) ) ;
}
// The holes are saved elevated above the mesh and deeper (bad idea indeed).
// This is retained for compatibility.
// Place the hole to the mesh and make it shallower to compensate.
// The offset is 1 mm above the mesh.
for ( sla : : DrainHole & hole : sla_drain_holes ) {
hole . pos + = hole . normal . normalized ( ) ;
hole . height - = 1.f ;
}
if ( ! sla_drain_holes . empty ( ) )
m_sla_drain_holes . insert ( { object_id , sla_drain_holes } ) ;
}
}
} */
void _QDS_3MF_Importer : : _extract_custom_gcode_per_print_z_from_archive ( : : mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
{
//QDS: add plate tree related logic
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 ) ;
if ( main_tree . front ( ) . first ! = " custom_gcodes_per_layer " )
return ;
auto extract_code = [ this ] ( int plate_id , pt : : ptree code_tree ) {
for ( const auto & code : code_tree ) {
if ( code . first = = " mode " ) {
pt : : ptree tree = code . second ;
std : : string mode = tree . get < std : : string > ( " <xmlattr>.value " ) ;
m_model - > plates_custom_gcodes [ plate_id - 1 ] . mode = mode = = CustomGCode : : SingleExtruderMode ? CustomGCode : : Mode : : SingleExtruder :
mode = = CustomGCode : : MultiAsSingleMode ? CustomGCode : : Mode : : MultiAsSingle :
CustomGCode : : Mode : : MultiExtruder ;
}
if ( code . first = = " layer " ) {
pt : : ptree tree = code . second ;
double print_z = tree . get < double > ( " <xmlattr>.top_z " ) ;
int extruder = tree . get < int > ( " <xmlattr>.extruder " ) ;
std : : string color = tree . get < std : : string > ( " <xmlattr>.color " ) ;
CustomGCode : : Type type ;
std : : string extra ;
pt : : ptree attr_tree = tree . find ( " <xmlattr> " ) - > second ;
if ( attr_tree . find ( " type " ) = = attr_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 - > plates_custom_gcodes [ plate_id - 1 ] . gcodes . push_back ( CustomGCode : : Item { print_z , type , extruder , color , extra } ) ;
}
}
} ;
m_model - > plates_custom_gcodes . clear ( ) ;
bool has_plate_info = false ;
for ( const auto & element : main_tree . front ( ) . second ) {
if ( element . first = = " plate " ) {
has_plate_info = true ;
int plate_id = - 1 ;
pt : : ptree code_tree = element . second ;
for ( const auto & code : code_tree ) {
if ( code . first = = " plate_info " ) {
plate_id = code . second . get < int > ( " <xmlattr>.id " ) ;
}
}
if ( plate_id = = - 1 )
continue ;
extract_code ( plate_id , code_tree ) ;
}
}
if ( ! has_plate_info ) {
int plate_id = 1 ;
pt : : ptree code_tree = main_tree . front ( ) . second ;
extract_code ( plate_id , code_tree ) ;
}
}
}
void _QDS_3MF_Importer : : _handle_start_model_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 ( 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 ( COLOR_GROUP_TAG , name ) = = 0 )
res = _handle_start_color_group ( attributes , num_attributes ) ;
else if ( : : strcmp ( COLOR_TAG , name ) = = 0 )
res = _handle_start_color ( 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 ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_start_metadata ( attributes , num_attributes ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
void _QDS_3MF_Importer : : _handle_end_model_xml_element ( const char * name )
{
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 ( COLOR_GROUP_TAG , name ) = = 0 )
res = _handle_end_color_group ( ) ;
else if ( : : strcmp ( COLOR_TAG , name ) = = 0 )
res = _handle_end_color ( ) ;
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 ( ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_end_metadata ( ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
void _QDS_3MF_Importer : : _handle_xml_characters ( const XML_Char * s , int len )
{
m_curr_characters . append ( s , len ) ;
}
void _QDS_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 ( PART_TAG , name ) = = 0 )
res = _handle_start_config_volume ( attributes , num_attributes ) ;
else if ( : : strcmp ( MESH_STAT_TAG , name ) = = 0 )
res = _handle_start_config_volume_mesh ( attributes , num_attributes ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_start_config_metadata ( attributes , num_attributes ) ;
else if ( : : strcmp ( PLATE_TAG , name ) = = 0 )
res = _handle_start_config_plater ( attributes , num_attributes ) ;
else if ( : : strcmp ( INSTANCE_TAG , name ) = = 0 )
res = _handle_start_config_plater_instance ( attributes , num_attributes ) ;
else if ( : : strcmp ( FILAMENT_TAG , name ) = = 0 )
res = _handle_start_config_filament ( attributes , num_attributes ) ;
else if ( : : strcmp ( SLICE_WARNING_TAG , name ) = = 0 )
res = _handle_start_config_warning ( attributes , num_attributes ) ;
else if ( : : strcmp ( ASSEMBLE_TAG , name ) = = 0 )
res = _handle_start_assemble ( attributes , num_attributes ) ;
else if ( : : strcmp ( ASSEMBLE_ITEM_TAG , name ) = = 0 )
res = _handle_start_assemble_item ( attributes , num_attributes ) ;
else if ( : : strcmp ( TEXT_INFO_TAG , name ) = = 0 )
res = _handle_start_text_info_item ( attributes , num_attributes ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
void _QDS_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 ( PART_TAG , name ) = = 0 )
res = _handle_end_config_volume ( ) ;
else if ( : : strcmp ( MESH_STAT_TAG , name ) = = 0 )
res = _handle_end_config_volume_mesh ( ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_end_config_metadata ( ) ;
else if ( : : strcmp ( PLATE_TAG , name ) = = 0 )
res = _handle_end_config_plater ( ) ;
else if ( : : strcmp ( FILAMENT_TAG , name ) = = 0 )
res = _handle_end_config_filament ( ) ;
else if ( : : strcmp ( INSTANCE_TAG , name ) = = 0 )
res = _handle_end_config_plater_instance ( ) ;
else if ( : : strcmp ( ASSEMBLE_TAG , name ) = = 0 )
res = _handle_end_assemble ( ) ;
else if ( : : strcmp ( ASSEMBLE_ITEM_TAG , name ) = = 0 )
res = _handle_end_assemble_item ( ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
bool _QDS_3MF_Importer : : _handle_start_model ( const char * * attributes , unsigned int num_attributes )
{
m_unit_factor = qds_get_unit_factor ( qds_get_attribute_value_string ( attributes , num_attributes , UNIT_ATTR ) ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_model ( )
{
// QDS: Production Extension
if ( ! m_sub_model_path . empty ( ) )
return true ;
// deletes all non-built or non-instanced objects
for ( const IdToModelObjectMap : : value_type & object : m_objects ) {
if ( object . second > = int ( m_model - > objects . size ( ) ) ) {
add_error ( " Unable to find object " ) ;
return false ;
}
ModelObject * model_object = m_model - > objects [ object . second ] ;
if ( model_object ! = nullptr & & model_object - > instances . size ( ) = = 0 )
m_model - > delete_object ( model_object ) ;
}
//construct the index maps
for ( const IdToCurrentObjectMap : : value_type & object : m_current_objects ) {
m_index_paths . insert ( { object . first . second , object . first . first } ) ;
}
if ( ! m_is_qdt_3mf ) {
// if the 3mf was not produced by QIDIStudio and there is only one object,
// set the object name to match the filename
if ( m_model - > objects . size ( ) = = 1 )
m_model - > objects . front ( ) - > name = m_name ;
}
// applies instances' matrices
for ( Instance & instance : m_instances ) {
if ( instance . instance ! = nullptr & & instance . instance - > get_object ( ) ! = nullptr )
// apply the transform to the instance
_apply_transform ( * instance . instance , instance . transform ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_resources ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_resources ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_object ( const char * * attributes , unsigned int num_attributes )
{
// reset current object data
if ( m_curr_object ) {
delete m_curr_object ;
m_curr_object = nullptr ;
}
std : : string object_type = qds_get_attribute_value_string ( attributes , num_attributes , TYPE_ATTR ) ;
if ( qds_is_valid_object_type ( object_type ) ) {
if ( ! m_curr_object ) {
m_curr_object = new CurrentObject ( ) ;
// create new object (it may be removed later if no instances are generated from it)
/*m_curr_object->model_object_idx = (int)m_model->objects.size();
m_curr_object . object = m_model - > add_object ( ) ;
if ( m_curr_object . object = = nullptr ) {
add_error ( " Unable to create object " ) ;
return false ;
} */
}
m_curr_object - > id = qds_get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
m_curr_object - > name = qds_get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
m_curr_object - > uuid = qds_get_attribute_value_string ( attributes , num_attributes , PUUID_ATTR ) ;
if ( m_curr_object - > uuid . empty ( ) ) {
m_curr_object - > uuid = qds_get_attribute_value_string ( attributes , num_attributes , PUUID_LOWER_ATTR ) ;
}
m_curr_object - > pid = qds_get_attribute_value_int ( attributes , num_attributes , PID_ATTR ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_object ( )
{
if ( ! m_load_model ) {
delete m_curr_object ;
m_curr_object = nullptr ;
return true ;
}
if ( ! m_curr_object | | ( m_curr_object - > id = = - 1 ) ) {
add_error ( " Found invalid object " ) ;
return false ;
}
else {
if ( m_is_qdt_3mf & & boost : : ends_with ( m_curr_object - > uuid , OBJECT_UUID_SUFFIX ) & & m_load_restore ) {
// Adjust backup object/volume id
std : : istringstream iss ( m_curr_object - > uuid ) ;
int backup_id ;
bool need_replace = false ;
if ( iss > > std : : hex > > backup_id ) {
need_replace = ( m_curr_object - > id ! = backup_id ) ;
m_curr_object - > id = backup_id ;
}
if ( ! m_curr_object - > components . empty ( ) )
{
Id first_id = m_curr_object - > components . front ( ) . object_id ;
first_id . second = 0 ;
IdToCurrentObjectMap : : iterator current_object = m_current_objects . lower_bound ( first_id ) ;
IdToCurrentObjectMap new_map ;
for ( int index = 0 ; index < m_curr_object - > components . size ( ) ; index + + )
{
Component & component = m_curr_object - > components [ index ] ;
Id new_id = component . object_id ;
new_id . second = ( index + 1 ) < < 16 | backup_id ;
if ( current_object ! = m_current_objects . end ( )
& & ( new_id . first . empty ( ) | | new_id . first = = current_object - > first . first ) ) {
current_object - > second . id = new_id . second ;
new_map . insert ( { new_id , std : : move ( current_object - > second ) } ) ;
current_object = m_current_objects . erase ( current_object ) ;
}
else {
add_error ( " can not find object for component, id= " + std : : to_string ( component . object_id . second ) ) ;
delete m_curr_object ;
m_curr_object = nullptr ;
return false ;
}
component . object_id . second = new_id . second ;
}
for ( auto & obj : new_map )
m_current_objects . insert ( { obj . first , std : : move ( obj . second ) } ) ;
}
}
Id id = std : : make_pair ( m_sub_model_path , m_curr_object - > id ) ;
if ( m_current_objects . find ( id ) = = m_current_objects . end ( ) ) {
m_current_objects . insert ( { id , std : : move ( * m_curr_object ) } ) ;
delete m_curr_object ;
m_curr_object = nullptr ;
}
else {
add_error ( " Found object with duplicate id " ) ;
delete m_curr_object ;
m_curr_object = nullptr ;
return false ;
}
}
/*if (m_curr_object.object != nullptr) {
if ( m_curr_object . id ! = - 1 ) {
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 ( id ) ;
if ( object_item ! = m_objects . end ( ) )
m_objects . erase ( object_item ) ;
IdToAliasesMap : : iterator alias_item = m_objects_aliases . find ( id ) ;
if ( alias_item ! = m_objects_aliases . end ( ) )
m_objects_aliases . erase ( alias_item ) ;
}
else
// adds components to aliases
m_objects_aliases . insert ( { id , m_curr_object . components } ) ;
}
else {
// geometry defined, store it for later use
m_geometries . insert ( { id , std : : move ( m_curr_object . geometry ) } ) ;
// stores the object for later use
if ( m_objects . find ( id ) = = m_objects . end ( ) ) {
m_objects . insert ( { id , m_curr_object . model_object_idx } ) ;
m_objects_aliases . insert ( { id , { 1 , Component ( m_curr_object . id ) } } ) ; // aliases itself
}
else {
add_error ( " Found object with duplicate id " ) ;
return false ;
}
}
}
else {
//sub objects
}
} */
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_color_group ( const char * * attributes , unsigned int num_attributes )
{
m_current_color_group = qds_get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_color_group ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_color ( const char * * attributes , unsigned int num_attributes )
{
std : : string color = qds_get_attribute_value_string ( attributes , num_attributes , COLOR_ATTR ) ;
m_group_id_to_color [ m_current_color_group ] = color ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_color ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_mesh ( const char * * attributes , unsigned int num_attributes )
{
// reset current geometry
if ( m_curr_object )
m_curr_object - > geometry . reset ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_mesh ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_vertices ( const char * * attributes , unsigned int num_attributes )
{
// reset current vertices
if ( m_curr_object )
m_curr_object - > geometry . vertices . clear ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_vertices ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_vertex ( const char * * attributes , unsigned int num_attributes )
{
// appends the vertex coordinates
// missing values are set equal to ZERO
if ( m_curr_object )
m_curr_object - > geometry . vertices . emplace_back (
m_unit_factor * qds_get_attribute_value_float ( attributes , num_attributes , X_ATTR ) ,
m_unit_factor * qds_get_attribute_value_float ( attributes , num_attributes , Y_ATTR ) ,
m_unit_factor * qds_get_attribute_value_float ( attributes , num_attributes , Z_ATTR ) ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_vertex ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_triangles ( const char * * attributes , unsigned int num_attributes )
{
// reset current triangles
if ( m_curr_object )
m_curr_object - > geometry . triangles . clear ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_triangles ( )
{
// do nothing
return true ;
}
bool _QDS_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
if ( m_curr_object ) {
m_curr_object - > geometry . triangles . emplace_back (
qds_get_attribute_value_int ( attributes , num_attributes , V1_ATTR ) ,
qds_get_attribute_value_int ( attributes , num_attributes , V2_ATTR ) ,
qds_get_attribute_value_int ( attributes , num_attributes , V3_ATTR ) ) ;
m_curr_object - > geometry . custom_supports . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , CUSTOM_SUPPORTS_ATTR ) ) ;
m_curr_object - > geometry . custom_seam . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , CUSTOM_SEAM_ATTR ) ) ;
m_curr_object - > geometry . mmu_segmentation . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , MMU_SEGMENTATION_ATTR ) ) ;
// QDS
m_curr_object - > geometry . face_properties . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , FACE_PROPERTY_ATTR ) ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_triangle ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_components ( const char * * attributes , unsigned int num_attributes )
{
// reset current components
if ( m_curr_object )
m_curr_object - > components . clear ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_components ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_component ( const char * * attributes , unsigned int num_attributes )
{
std : : string path = xml_unescape ( qds_get_attribute_value_string ( attributes , num_attributes , PPATH_ATTR ) ) ;
int object_id = qds_get_attribute_value_int ( attributes , num_attributes , OBJECTID_ATTR ) ;
Transform3d transform = qds_get_transform_from_3mf_specs_string ( qds_get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
/*Id id = std::make_pair(m_sub_model_path, object_id);
IdToModelObjectMap : : iterator object_item = m_objects . find ( id ) ;
if ( object_item = = m_objects . end ( ) ) {
IdToAliasesMap : : iterator alias_item = m_objects_aliases . find ( id ) ;
if ( alias_item = = m_objects_aliases . end ( ) ) {
add_error ( " Found component with invalid object id " ) ;
return false ;
}
} */
if ( m_curr_object ) {
Id id = std : : make_pair ( m_sub_model_path . empty ( ) ? path : m_sub_model_path , object_id ) ;
m_curr_object - > components . emplace_back ( id , transform ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_component ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_build ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_build ( )
{
// do nothing
return true ;
}
bool _QDS_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 = qds_get_attribute_value_int ( attributes , num_attributes , OBJECTID_ATTR ) ;
std : : string path = qds_get_attribute_value_string ( attributes , num_attributes , PPATH_ATTR ) ;
Transform3d transform = qds_get_transform_from_3mf_specs_string ( qds_get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
int printable = qds_get_attribute_value_bool ( attributes , num_attributes , PRINTABLE_ATTR ) ;
return ! m_load_model | | _create_object_instance ( path , object_id , transform , printable , 1 ) ;
}
bool _QDS_3MF_Importer : : _handle_end_item ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_metadata ( const char * * attributes , unsigned int num_attributes )
{
m_curr_characters . clear ( ) ;
std : : string name = qds_get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
if ( ! name . empty ( ) ) {
m_curr_metadata_name = name ;
}
return true ;
}
inline static void check_painting_version ( unsigned int loaded_version , unsigned int highest_supported_version , const std : : string & error_msg )
{
if ( loaded_version > highest_supported_version )
throw version_error ( error_msg ) ;
}
bool _QDS_3MF_Importer : : _handle_end_metadata ( )
{
if ( ( m_curr_metadata_name = = QDS_3MF_VERSION ) | | ( m_curr_metadata_name = = QDS_3MF_VERSION1 ) ) {
//m_is_qdt_3mf = true;
m_version = ( unsigned int ) atoi ( m_curr_characters . c_str ( ) ) ;
/*if (m_check_version && (m_version > VERSION_QDS_3MF_COMPATIBLE)) {
// 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 ) ;
} */
} else if ( m_curr_metadata_name = = QDT_APPLICATION_TAG ) {
// Generator application of the 3MF.
// SLIC3R_APP_KEY - SLIC3R_VERSION
if ( boost : : starts_with ( m_curr_characters , " QIDIStudio- " ) ) {
m_is_qdt_3mf = true ;
m_qidislicer_generator_version = Semver : : parse ( m_curr_characters . substr ( 12 ) ) ;
}
//TODO: currently use version 0, no need to load&&save this string
/*} else if (m_curr_metadata_name == QDS_FDM_SUPPORTS_PAINTING_VERSION) {
m_fdm_supports_painting_version = ( unsigned int ) atoi ( m_curr_characters . c_str ( ) ) ;
check_painting_version ( m_fdm_supports_painting_version , FDM_SUPPORTS_PAINTING_VERSION ,
_ ( L ( " The selected 3MF contains FDM supports painted object using a newer version of QIDIStudio and is not compatible. " ) ) ) ;
} else if ( m_curr_metadata_name = = QDS_SEAM_PAINTING_VERSION ) {
m_seam_painting_version = ( unsigned int ) atoi ( m_curr_characters . c_str ( ) ) ;
check_painting_version ( m_seam_painting_version , SEAM_PAINTING_VERSION ,
_ ( L ( " The selected 3MF contains seam painted object using a newer version of QIDIStudio and is not compatible. " ) ) ) ;
} else if ( m_curr_metadata_name = = QDS_MM_PAINTING_VERSION ) {
m_mm_painting_version = ( unsigned int ) atoi ( m_curr_characters . c_str ( ) ) ;
check_painting_version ( m_mm_painting_version , MM_PAINTING_VERSION ,
_ ( L ( " The selected 3MF contains multi-material painted object using a newer version of QIDIStudio and is not compatible. " ) ) ) ; */
} else if ( m_curr_metadata_name = = QDT_MODEL_ID_TAG ) {
m_model_id = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_MODEL_NAME_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found model name = " < < m_curr_characters ;
model_info . model_name = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_ORIGIN_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found model name = " < < m_curr_characters ;
model_info . origin = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_DESIGNER_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found designer = " < < m_curr_characters ;
m_designer = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_DESIGNER_USER_ID_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found designer_user_id = " < < m_curr_characters ;
m_designer_user_id = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_DESIGNER_MODEL_ID_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found designer_model_id = " < < m_curr_characters ;
m_designer_id = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_DESIGN_ID_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found design_id = " < < m_curr_characters ;
m_design_id = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_DESIGNER_COVER_FILE_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found designer_cover = " < < m_curr_characters ;
model_info . cover_file = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_DESCRIPTION_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found description = " < < m_curr_characters ;
model_info . description = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_LICENSE_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found license = " < < m_curr_characters ;
model_info . license = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_COPYRIGHT_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found CopyRight = " < < m_curr_characters ;
model_info . copyright = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_COPYRIGHT_NORMATIVE_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found Copyright = " < < m_curr_characters ;
model_info . copyright = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_REGION_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found region = " < < m_curr_characters ;
m_contry_code = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_PROFILE_TITLE_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found profile_title = " < < m_curr_characters ;
m_profile_title = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_PROFILE_COVER_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found profile_cover = " < < m_curr_characters ;
m_profile_cover = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_PROFILE_DESCRIPTION_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found profile_description = " < < m_curr_characters ;
m_Profile_description = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_PROFILE_USER_ID_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found profile_user_id = " < < m_curr_characters ;
m_profile_user_id = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_PROFILE_USER_NAME_TAG ) {
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, load_3mf found profile_user_name = " < < m_curr_characters ;
m_profile_user_name = xml_unescape ( m_curr_characters ) ;
} else if ( m_curr_metadata_name = = QDT_CREATION_DATE_TAG ) {
;
} else if ( m_curr_metadata_name = = QDT_MODIFICATION_TAG ) {
;
} else {
;
}
if ( ! m_curr_metadata_name . empty ( ) ) {
model_info . metadata_items [ m_curr_metadata_name ] = xml_unescape ( m_curr_characters ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _create_object_instance ( std : : string const & path , int object_id , const Transform3d & transform , const bool printable , unsigned int recur_counter )
{
static const unsigned int MAX_RECURSIONS = 10 ;
// escape from circular aliasing
if ( recur_counter > MAX_RECURSIONS ) {
add_error ( " Too many recursions " ) ;
return false ;
}
Id id { path , object_id } ;
IdToCurrentObjectMap : : iterator it = m_current_objects . find ( id ) ;
if ( it = = m_current_objects . end ( ) ) {
add_error ( " can not find object id " + std : : to_string ( object_id ) + " to builditem " ) ;
return false ;
}
IdToModelObjectMap : : iterator object_item = m_objects . find ( id ) ;
if ( object_item = = m_objects . end ( ) ) {
//add object
CurrentObject & current_object = it - > second ;
int object_index = ( int ) m_model - > objects . size ( ) ;
ModelObject * model_object = m_model - > add_object ( ) ;
if ( model_object = = nullptr ) {
add_error ( " Unable to create object for builditem, id " + std : : to_string ( object_id ) ) ;
return false ;
}
m_objects . insert ( { id , object_index } ) ;
current_object . model_object_idx = object_index ;
current_object . object = model_object ;
ModelInstance * instance = m_model - > objects [ object_index ] - > add_instance ( ) ;
if ( instance = = nullptr ) {
add_error ( " error when add object instance for id " + std : : to_string ( object_id ) ) ;
return false ;
}
instance - > printable = printable ;
m_instances . emplace_back ( instance , transform ) ;
if ( m_is_qdt_3mf & & boost : : ends_with ( current_object . uuid , OBJECT_UUID_SUFFIX ) ) {
std : : istringstream iss ( current_object . uuid ) ;
int backup_id ;
if ( iss > > std : : hex > > backup_id ) {
m_model - > set_object_backup_id ( * model_object , backup_id ) ;
}
}
/*if (!current_object.geometry.empty()) {
}
else if ( ! current_object . components . empty ( ) ) {
// recursively process nested components
for ( const Component & component : it - > second ) {
if ( ! _create_object_instance ( path , component . object_id , transform * component . transform , printable , recur_counter + 1 ) )
return false ;
}
}
else {
add_error ( " can not construct build items with invalid object, id " + std : : to_string ( object_id ) ) ;
return false ;
} */
}
else {
//add instance
ModelInstance * instance = m_model - > objects [ object_item - > second ] - > add_instance ( ) ;
if ( instance = = nullptr ) {
add_error ( " error when add object instance for id " + std : : to_string ( object_id ) ) ;
return false ;
}
instance - > printable = printable ;
m_instances . emplace_back ( instance , transform ) ;
}
/*if (it->second.size() == 1 && it->second[0].object_id == object_id) {
// aliasing to itself
IdToModelObjectMap : : iterator object_item = m_objects . find ( id ) ;
if ( object_item = = m_objects . end ( ) | | object_item - > second = = - 1 ) {
add_error ( " Found invalid object " ) ;
return false ;
}
else {
ModelInstance * instance = m_model - > objects [ object_item - > second ] - > add_instance ( ) ;
if ( instance = = nullptr ) {
add_error ( " Unable to add object instance " ) ;
return false ;
}
instance - > printable = printable ;
m_instances . emplace_back ( instance , transform ) ;
}
}
else {
// recursively process nested components
for ( const Component & component : it - > second ) {
if ( ! _create_object_instance ( path , component . object_id , transform * component . transform , printable , recur_counter + 1 ) )
return false ;
}
} */
return true ;
}
void _QDS_3MF_Importer : : _apply_transform ( ModelInstance & instance , const Transform3d & transform )
{
Slic3r : : Geometry : : Transformation t ( transform ) ;
// invalid scale value, return
if ( ! t . get_scaling_factor ( ) . all ( ) )
return ;
instance . set_transformation ( t ) ;
}
bool _QDS_3MF_Importer : : _handle_start_config ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_object ( const char * * attributes , unsigned int num_attributes )
{
if ( m_parsing_slice_info )
return true ;
int object_id = qds_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 ( " Duplicated object id: " + std : : to_string ( object_id ) + " in model_settings.config " ) ;
return false ;
}
// Added because of github #3435, currently not used by PrusaSlicer
// int instances_count_id = qds_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
m_objects_metadata . insert ( { object_id , ObjectMetadata ( ) } ) ;
m_curr_config . object_id = object_id ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_object ( )
{
m_curr_config . object_id = - 1 ;
return true ;
}
bool _QDS_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 ( " can not find object for part, id " + std : : to_string ( m_curr_config . object_id ) ) ;
return false ;
}
m_curr_config . volume_id = ( int ) object - > second . volumes . size ( ) ;
unsigned int first_triangle_id = ( unsigned int ) qds_get_attribute_value_int ( attributes , num_attributes , FIRST_TRIANGLE_ID_ATTR ) ;
unsigned int last_triangle_id = ( unsigned int ) qds_get_attribute_value_int ( attributes , num_attributes , LAST_TRIANGLE_ID_ATTR ) ;
//QDS: refine the part type logic
std : : string subtype_str = qds_get_attribute_value_string ( attributes , num_attributes , SUBTYPE_ATTR ) ;
ModelVolumeType type = ModelVolume : : type_from_string ( subtype_str ) ;
int subbject_id = qds_get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
if ( last_triangle_id > 0 )
object - > second . volumes . emplace_back ( first_triangle_id , last_triangle_id , type ) ;
else
object - > second . volumes . emplace_back ( subbject_id , type ) ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_volume_mesh ( 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 ( " can not find object for mesh_stats, id " + std : : to_string ( m_curr_config . object_id ) ) ;
return false ;
}
if ( ( m_curr_config . volume_id = = - 1 ) | | ( ( object - > second . volumes . size ( ) - 1 ) < m_curr_config . volume_id ) ) {
add_error ( " can not find part for mesh_stats " ) ;
return false ;
}
ObjectMetadata : : VolumeMetadata & volume = object - > second . volumes [ m_curr_config . volume_id ] ;
int edges_fixed = qds_get_attribute_value_int ( attributes , num_attributes , MESH_STAT_EDGES_FIXED ) ;
int degenerate_facets = qds_get_attribute_value_int ( attributes , num_attributes , MESH_STAT_DEGENERATED_FACETS ) ;
int facets_removed = qds_get_attribute_value_int ( attributes , num_attributes , MESH_STAT_FACETS_REMOVED ) ;
int facets_reversed = qds_get_attribute_value_int ( attributes , num_attributes , MESH_STAT_FACETS_RESERVED ) ;
int backwards_edges = qds_get_attribute_value_int ( attributes , num_attributes , MESH_STAT_BACKWARDS_EDGES ) ;
volume . mesh_stats = { edges_fixed , degenerate_facets , facets_removed , facets_reversed , backwards_edges } ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_volume ( )
{
m_curr_config . volume_id = - 1 ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_volume_mesh ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_metadata ( const char * * attributes , unsigned int num_attributes )
{
//std::string type = qds_get_attribute_value_string(attributes, num_attributes, TYPE_ATTR);
std : : string key = qds_get_attribute_value_string ( attributes , num_attributes , KEY_ATTR ) ;
std : : string value = qds_get_attribute_value_string ( attributes , num_attributes , VALUE_ATTR ) ;
if ( ( m_curr_plater = = nullptr ) & & ! m_parsing_slice_info )
{
IdToMetadataMap : : iterator object = m_objects_metadata . find ( m_curr_config . object_id ) ;
if ( object = = m_objects_metadata . end ( ) ) {
add_error ( " Cannot find object for metadata, id " + std : : to_string ( m_curr_config . object_id ) ) ;
return false ;
}
if ( m_curr_config . volume_id = = - 1 )
object - > second . metadata . emplace_back ( key , value ) ;
else {
if ( size_t ( m_curr_config . volume_id ) < object - > second . volumes . size ( ) )
object - > second . volumes [ m_curr_config . volume_id ] . metadata . emplace_back ( key , value ) ;
}
}
else
{
//plater
if ( key = = PLATERID_ATTR )
{
m_curr_plater - > plate_index = atoi ( value . c_str ( ) ) ;
}
else if ( key = = PLATER_NAME_ATTR ) {
m_curr_plater - > plate_name = xml_unescape ( value . c_str ( ) ) ;
}
else if ( key = = LOCK_ATTR )
{
std : : istringstream ( value ) > > std : : boolalpha > > m_curr_plater - > locked ;
}
else if ( key = = BED_TYPE_ATTR )
{
BedType bed_type = BedType : : btPC ;
ConfigOptionEnum < BedType > : : from_string ( value , bed_type ) ;
m_curr_plater - > config . set_key_value ( " curr_bed_type " , new ConfigOptionEnum < BedType > ( bed_type ) ) ;
}
else if ( key = = PRINT_SEQUENCE_ATTR )
{
PrintSequence print_sequence = PrintSequence : : ByLayer ;
ConfigOptionEnum < PrintSequence > : : from_string ( value , print_sequence ) ;
m_curr_plater - > config . set_key_value ( " print_sequence " , new ConfigOptionEnum < PrintSequence > ( print_sequence ) ) ;
}
else if ( key = = FIRST_LAYER_PRINT_SEQUENCE_ATTR ) {
auto get_vector_from_string = [ ] ( const std : : string & str ) - > std : : vector < int > {
std : : stringstream stream ( str ) ;
int value ;
std : : vector < int > results ;
while ( stream > > value ) {
results . push_back ( value ) ;
}
return results ;
} ;
m_curr_plater - > config . set_key_value ( " first_layer_print_sequence " , new ConfigOptionInts ( get_vector_from_string ( value ) ) ) ;
}
else if ( key = = OTHER_LAYERS_PRINT_SEQUENCE_ATTR ) {
auto get_vector_from_string = [ ] ( const std : : string & str ) - > std : : vector < int > {
std : : stringstream stream ( str ) ;
int value ;
std : : vector < int > results ;
while ( stream > > value ) { results . push_back ( value ) ; }
return results ;
} ;
m_curr_plater - > config . set_key_value ( " other_layers_print_sequence " , new ConfigOptionInts ( get_vector_from_string ( value ) ) ) ;
}
else if ( key = = OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR ) {
m_curr_plater - > config . set_key_value ( " other_layers_print_sequence_nums " , new ConfigOptionInt ( stoi ( value ) ) ) ;
}
else if ( key = = SPIRAL_VASE_MODE ) {
bool spiral_mode = false ;
std : : istringstream ( value ) > > std : : boolalpha > > spiral_mode ;
m_curr_plater - > config . set_key_value ( " spiral_mode " , new ConfigOptionBool ( spiral_mode ) ) ;
}
else if ( key = = GCODE_FILE_ATTR )
{
m_curr_plater - > gcode_file = value ;
}
else if ( key = = THUMBNAIL_FILE_ATTR )
{
m_curr_plater - > thumbnail_file = value ;
}
else if ( key = = NO_LIGHT_THUMBNAIL_FILE_ATTR )
{
m_curr_plater - > no_light_thumbnail_file = value ;
}
else if ( key = = TOP_FILE_ATTR )
{
m_curr_plater - > top_file = value ;
}
else if ( key = = PICK_FILE_ATTR )
{
m_curr_plater - > pick_file = value ;
}
//else if (key == PATTERN_FILE_ATTR)
//{
// m_curr_plater->pattern_file = value;
//}
else if ( key = = PATTERN_BBOX_FILE_ATTR )
{
m_curr_plater - > pattern_bbox_file = value ;
}
else if ( key = = INSTANCEID_ATTR )
{
m_curr_instance . instance_id = atoi ( value . c_str ( ) ) ;
}
else if ( key = = IDENTIFYID_ATTR )
{
m_curr_instance . identify_id = atoi ( value . c_str ( ) ) ;
}
else if ( key = = OBJECT_ID_ATTR )
{
m_curr_instance . object_id = atoi ( value . c_str ( ) ) ;
/*int obj_id = atoi(value.c_str());
m_curr_instance . object_id = - 1 ;
IndexToPathMap : : iterator index_iter = m_index_paths . find ( obj_id ) ;
if ( index_iter = = m_index_paths . end ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__
< < boost : : format ( " , can not find object for plate's item, id=%1%, skip this object " ) % obj_id ;
return true ;
}
Id temp_id = std : : make_pair ( index_iter - > second , index_iter - > first ) ;
IdToModelObjectMap : : iterator object_item = m_objects . find ( temp_id ) ;
if ( object_item = = m_objects . end ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__
< < boost : : format ( " , can not find object for plate's item, ID <%1%, %2%>, skip this object " ) % index_iter - > second % index_iter - > first ;
return true ;
}
m_curr_instance . object_id = object_item - > second ; */
}
else if ( key = = PLATE_IDX_ATTR )
{
int plate_index = atoi ( value . c_str ( ) ) ;
std : : map < int , PlateData * > : : iterator it = m_plater_data . find ( plate_index ) ;
if ( it ! = m_plater_data . end ( ) )
m_curr_plater = it - > second ;
}
else if ( key = = SLICE_PREDICTION_ATTR )
{
if ( m_curr_plater )
m_curr_plater - > gcode_prediction = value ;
}
else if ( key = = SLICE_WEIGHT_ATTR )
{
if ( m_curr_plater )
m_curr_plater - > gcode_weight = value ;
}
else if ( key = = OUTSIDE_ATTR )
{
if ( m_curr_plater )
std : : istringstream ( value ) > > std : : boolalpha > > m_curr_plater - > toolpath_outside ;
}
else if ( key = = SUPPORT_USED_ATTR )
{
if ( m_curr_plater )
std : : istringstream ( value ) > > std : : boolalpha > > m_curr_plater - > is_support_used ;
}
else if ( key = = LABEL_OBJECT_ENABLED_ATTR )
{
if ( m_curr_plater )
std : : istringstream ( value ) > > std : : boolalpha > > m_curr_plater - > is_label_object_enabled ;
}
else if ( key = = PRINTER_MODEL_ID_ATTR )
{
if ( m_curr_plater )
m_curr_plater - > printer_model_id = value ;
}
else if ( key = = NOZZLE_DIAMETERS_ATTR )
{
if ( m_curr_plater )
m_curr_plater - > nozzle_diameters = value ;
}
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_metadata ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_filament ( const char * * attributes , unsigned int num_attributes )
{
if ( m_curr_plater ) {
std : : string id = qds_get_attribute_value_string ( attributes , num_attributes , FILAMENT_ID_TAG ) ;
std : : string type = qds_get_attribute_value_string ( attributes , num_attributes , FILAMENT_TYPE_TAG ) ;
std : : string color = qds_get_attribute_value_string ( attributes , num_attributes , FILAMENT_COLOR_TAG ) ;
std : : string used_m = qds_get_attribute_value_string ( attributes , num_attributes , FILAMENT_USED_M_TAG ) ;
std : : string used_g = qds_get_attribute_value_string ( attributes , num_attributes , FILAMENT_USED_G_TAG ) ;
std : : string filament_id = qds_get_attribute_value_string ( attributes , num_attributes , FILAMENT_TRAY_INFO_ID_TAG ) ;
FilamentInfo filament_info ;
filament_info . id = atoi ( id . c_str ( ) ) - 1 ;
filament_info . type = type ;
filament_info . color = color ;
filament_info . used_m = atof ( used_m . c_str ( ) ) ;
filament_info . used_g = atof ( used_g . c_str ( ) ) ;
filament_info . filament_id = filament_id ;
m_curr_plater - > slice_filaments_info . push_back ( filament_info ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_filament ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_warning ( const char * * attributes , unsigned int num_attributes )
{
if ( m_curr_plater ) {
std : : string msg = qds_get_attribute_value_string ( attributes , num_attributes , WARNING_MSG_TAG ) ;
std : : string lvl_str = qds_get_attribute_value_string ( attributes , num_attributes , " level " ) ;
GCodeProcessorResult : : SliceWarning sw ;
sw . msg = msg ;
try {
sw . level = atoi ( lvl_str . c_str ( ) ) ;
}
catch ( . . . ) {
} ;
m_curr_plater - > warnings . push_back ( sw ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_warning ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_plater ( const char * * attributes , unsigned int num_attributes )
{
if ( ! m_parsing_slice_info ) {
m_curr_plater = new PlateData ( ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_plater ( )
{
if ( ! m_curr_plater )
{
add_error ( " _handle_end_config_plater: don't find plate created before " ) ;
return false ;
}
m_plater_data . emplace ( m_curr_plater - > plate_index , m_curr_plater ) ;
m_curr_plater = nullptr ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_config_plater_instance ( const char * * attributes , unsigned int num_attributes )
{
if ( ! m_curr_plater )
{
add_error ( " _handle_start_config_plater_instance: don't find plate created before " ) ;
return false ;
}
//do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_config_plater_instance ( )
{
if ( ! m_curr_plater )
{
add_error ( " _handle_end_config_plater_instance: don't find plate created before " ) ;
return false ;
}
if ( ( m_curr_instance . object_id = = - 1 ) | | ( m_curr_instance . instance_id = = - 1 ) )
{
//add_error("invalid object id/instance id");
//skip this instance
m_curr_instance . object_id = m_curr_instance . instance_id = - 1 ;
m_curr_instance . identify_id = 0 ;
return true ;
}
m_curr_plater - > obj_inst_map . emplace ( m_curr_instance . object_id , std : : make_pair ( m_curr_instance . instance_id , m_curr_instance . identify_id ) ) ;
m_curr_instance . object_id = m_curr_instance . instance_id = - 1 ;
m_curr_instance . identify_id = 0 ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_assemble ( const char * * attributes , unsigned int num_attributes )
{
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_assemble ( )
{
//do nothing
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_assemble_item ( const char * * attributes , unsigned int num_attributes )
{
if ( ! m_load_model ) return true ;
int object_id = qds_get_attribute_value_int ( attributes , num_attributes , OBJECT_ID_ATTR ) ;
int instance_id = qds_get_attribute_value_int ( attributes , num_attributes , INSTANCEID_ATTR ) ;
IndexToPathMap : : iterator index_iter = m_index_paths . find ( object_id ) ;
if ( index_iter = = m_index_paths . end ( ) ) {
add_error ( " can not find object for assemble item, id= " + std : : to_string ( object_id ) ) ;
return false ;
}
Id temp_id = std : : make_pair ( index_iter - > second , index_iter - > first ) ;
IdToModelObjectMap : : iterator object_item = m_objects . find ( temp_id ) ;
if ( object_item = = m_objects . end ( ) ) {
add_error ( " can not find object for assemble item, id= " + std : : to_string ( object_id ) ) ;
return false ;
}
object_id = object_item - > second ;
Transform3d transform = qds_get_transform_from_3mf_specs_string ( qds_get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
Vec3d ofs2ass = qds_get_offset_from_3mf_specs_string ( qds_get_attribute_value_string ( attributes , num_attributes , OFFSET_ATTR ) ) ;
if ( object_id < m_model - > objects . size ( ) ) {
if ( instance_id < m_model - > objects [ object_id ] - > instances . size ( ) ) {
m_model - > objects [ object_id ] - > instances [ instance_id ] - > set_assemble_from_transform ( transform ) ;
m_model - > objects [ object_id ] - > instances [ instance_id ] - > set_offset_to_assembly ( ofs2ass ) ;
}
}
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_assemble_item ( )
{
return true ;
}
bool _QDS_3MF_Importer : : _handle_start_text_info_item ( 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 ( " can not find object for text_info, id " + std : : to_string ( m_curr_config . object_id ) ) ;
return false ;
}
if ( ( m_curr_config . volume_id = = - 1 ) | | ( ( object - > second . volumes . size ( ) - 1 ) < m_curr_config . volume_id ) ) {
add_error ( " can not find part for text_info " ) ;
return false ;
}
ObjectMetadata : : VolumeMetadata & volume = object - > second . volumes [ m_curr_config . volume_id ] ;
TextInfo text_info ;
text_info . m_text = xml_unescape ( qds_get_attribute_value_string ( attributes , num_attributes , TEXT_ATTR ) ) ;
text_info . m_font_name = qds_get_attribute_value_string ( attributes , num_attributes , FONT_NAME_ATTR ) ;
text_info . m_curr_font_idx = qds_get_attribute_value_int ( attributes , num_attributes , FONT_INDEX_ATTR ) ;
text_info . m_font_size = qds_get_attribute_value_float ( attributes , num_attributes , FONT_SIZE_ATTR ) ;
text_info . m_thickness = qds_get_attribute_value_float ( attributes , num_attributes , THICKNESS_ATTR ) ;
text_info . m_embeded_depth = qds_get_attribute_value_float ( attributes , num_attributes , EMBEDED_DEPTH_ATTR ) ;
text_info . m_rotate_angle = qds_get_attribute_value_float ( attributes , num_attributes , ROTATE_ANGLE_ATTR ) ;
text_info . m_text_gap = qds_get_attribute_value_float ( attributes , num_attributes , TEXT_GAP_ATTR ) ;
text_info . m_bold = qds_get_attribute_value_int ( attributes , num_attributes , BOLD_ATTR ) ;
text_info . m_italic = qds_get_attribute_value_int ( attributes , num_attributes , ITALIC_ATTR ) ;
text_info . m_is_surface_text = qds_get_attribute_value_int ( attributes , num_attributes , SURFACE_TEXT_ATTR ) ;
text_info . m_keep_horizontal = qds_get_attribute_value_int ( attributes , num_attributes , KEEP_HORIZONTAL_ATTR ) ;
text_info . m_rr . mesh_id = qds_get_attribute_value_int ( attributes , num_attributes , HIT_MESH_ATTR ) ;
std : : string hit_pos = qds_get_attribute_value_string ( attributes , num_attributes , HIT_POSITION_ATTR ) ;
if ( ! hit_pos . empty ( ) )
text_info . m_rr . hit = get_vec3_from_string ( hit_pos ) ;
std : : string hit_normal = qds_get_attribute_value_string ( attributes , num_attributes , HIT_NORMAL_ATTR ) ;
if ( ! hit_normal . empty ( ) )
text_info . m_rr . normal = get_vec3_from_string ( hit_normal ) ;
volume . text_info = text_info ;
return true ;
}
bool _QDS_3MF_Importer : : _handle_end_text_info_item ( )
{
return true ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_start_relationships_element ( void * userData , const char * name , const char * * attributes )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_relationships_element ( name , attributes ) ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_end_relationships_element ( void * userData , const char * name )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_end_relationships_element ( name ) ;
}
void _QDS_3MF_Importer : : _handle_start_relationships_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 ( RELATIONSHIP_TAG , name ) = = 0 )
res = _handle_start_relationship ( attributes , num_attributes ) ;
m_curr_characters . clear ( ) ;
if ( ! res )
_stop_xml_parser ( ) ;
}
void _QDS_3MF_Importer : : _handle_end_relationships_element ( const char * name )
{
if ( m_xml_parser = = nullptr )
return ;
bool res = true ;
if ( ! res )
_stop_xml_parser ( ) ;
}
bool _QDS_3MF_Importer : : _handle_start_relationship ( const char * * attributes , unsigned int num_attributes )
{
std : : string path = qds_get_attribute_value_string ( attributes , num_attributes , TARGET_ATTR ) ;
std : : string type = qds_get_attribute_value_string ( attributes , num_attributes , RELS_TYPE_ATTR ) ;
if ( boost : : starts_with ( type , " http://schemas.microsoft.com/3dmanufacturing/ " ) & & boost : : ends_with ( type , " 3dmodel " ) ) {
if ( m_start_part_path . empty ( ) ) m_start_part_path = path ;
else m_sub_model_paths . push_back ( path ) ;
} else if ( boost : : starts_with ( type , " http://schemas.openxmlformats.org/ " ) & & boost : : ends_with ( type , " thumbnail " ) ) {
if ( boost : : algorithm : : ends_with ( path , " .png " ) )
m_thumbnail_path = path ;
} else if ( boost : : starts_with ( type , " http://schemas.qidilab.com/ " ) & & boost : : ends_with ( type , " cover-thumbnail-middle " ) ) {
m_thumbnail_middle = path ;
} else if ( boost : : starts_with ( type , " http://schemas.qidilab.com/ " ) & & boost : : ends_with ( type , " cover-thumbnail-small " ) ) {
m_thumbnail_small = path ;
}
return true ;
}
void _QDS_3MF_Importer : : _generate_current_object_list ( std : : vector < Component > & sub_objects , Id object_id , IdToCurrentObjectMap & current_objects )
{
std : : list < std : : pair < Component , Transform3d > > id_list ;
id_list . push_back ( std : : make_pair ( Component ( object_id , Transform3d : : Identity ( ) ) , Transform3d : : Identity ( ) ) ) ;
while ( ! id_list . empty ( ) )
{
auto current_item = id_list . front ( ) ;
Component current_id = current_item . first ;
id_list . pop_front ( ) ;
IdToCurrentObjectMap : : iterator current_object = current_objects . find ( current_id . object_id ) ;
if ( current_object ! = current_objects . end ( ) ) {
//found one
if ( ! current_object - > second . components . empty ( ) ) {
for ( const Component & comp : current_object - > second . components ) {
id_list . push_back ( std : : pair ( comp , current_item . second * comp . transform ) ) ;
}
}
else if ( ! ( current_object - > second . geometry . empty ( ) ) ) {
//CurrentObject* ptr = &(current_objects[current_id]);
//CurrentObject* ptr2 = &(current_object->second);
sub_objects . push_back ( { current_object - > first , current_item . second } ) ;
}
}
}
}
bool _QDS_3MF_Importer : : _generate_volumes_new ( ModelObject & object , const std : : vector < Component > & sub_objects , const ObjectMetadata : : VolumeMetadataList & volumes , ConfigSubstitutionContext & config_substitutions )
{
if ( ! object . volumes . empty ( ) ) {
add_error ( " object already built with parts " ) ;
return false ;
}
//unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
unsigned int renamed_volumes_count = 0 ;
for ( unsigned int index = 0 ; index < sub_objects . size ( ) ; index + + )
{
//find the volume metadata firstly
Component sub_comp = sub_objects [ index ] ;
Id object_id = sub_comp . object_id ;
IdToCurrentObjectMap : : iterator current_object = m_current_objects . find ( object_id ) ;
if ( current_object = = m_current_objects . end ( ) ) {
add_error ( " sub_objects can not be found, id= " + std : : to_string ( object_id . second ) ) ;
return false ;
}
CurrentObject * sub_object = & ( current_object - > second ) ;
const ObjectMetadata : : VolumeMetadata * volume_data = nullptr ;
ObjectMetadata : : VolumeMetadata default_volume_data ( sub_object - > id ) ;
if ( index < volumes . size ( ) & & volumes [ index ] . subobject_id = = sub_object - > id )
volume_data = & volumes [ index ] ;
else for ( const ObjectMetadata : : VolumeMetadata & volume_iter : volumes ) {
if ( volume_iter . subobject_id = = sub_object - > id ) {
volume_data = & volume_iter ;
break ;
}
}
Transform3d volume_matrix_to_object = Transform3d : : Identity ( ) ;
bool has_transform = false ;
int shared_mesh_id = object_id . second ;
if ( volume_data )
{
int found_count = 0 ;
// extract the volume transformation from the volume's metadata, if present
for ( const Metadata & metadata : volume_data - > metadata ) {
if ( metadata . key = = MATRIX_KEY ) {
volume_matrix_to_object = Slic3r : : Geometry : : transform3d_from_string ( metadata . value ) ;
has_transform = ! volume_matrix_to_object . isApprox ( Transform3d : : Identity ( ) , 1e-10 ) ;
found_count + + ;
}
else if ( metadata . key = = MESH_SHARED_KEY ) {
//add the shared mesh logic
shared_mesh_id = : : atoi ( metadata . value . c_str ( ) ) ;
found_count + + ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : line %1%, shared_mesh_id %2% " ) % __LINE__ % shared_mesh_id ;
}
if ( found_count > = 2 )
break ;
}
}
else {
//create a volume_data
volume_data = & default_volume_data ;
}
ModelVolume * volume = nullptr ;
ModelVolume * shared_volume = nullptr ;
BOOST_LOG_TRIVIAL ( debug ) < < __FUNCTION__ < < boost : : format ( " : line %1%, subobject_id %2%, shared_mesh_id %3% " ) % __LINE__ % sub_object - > id % shared_mesh_id ;
if ( shared_mesh_id ! = - 1 ) {
std : : map < int , ModelVolume * > : : iterator iter = m_shared_meshes . find ( shared_mesh_id ) ;
if ( iter ! = m_shared_meshes . end ( ) ) {
shared_volume = iter - > second ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : line %1%, found shared mesh, id %2%, mesh %3% " ) % __LINE__ % shared_mesh_id % shared_volume ;
}
}
else {
//for some cases, object point to this shared mesh already loaded, treat that one as the root
std : : map < int , ModelVolume * > : : iterator iter = m_shared_meshes . find ( sub_object - > id ) ;
if ( iter ! = m_shared_meshes . end ( ) ) {
shared_volume = iter - > second ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : line %1%, already loaded copy-share mesh before, id %2%, mesh %3% " ) % __LINE__ % sub_object - > id % shared_volume ;
}
}
const size_t triangles_count = sub_object - > geometry . triangles . size ( ) ;
if ( triangles_count = = 0 ) {
add_error ( " found no trianges in the object " + std : : to_string ( sub_object - > id ) ) ;
return false ;
}
if ( ! shared_volume ) {
// splits volume out of imported geometry
indexed_triangle_set its ;
its . indices . assign ( sub_object - > geometry . triangles . begin ( ) , sub_object - > geometry . triangles . end ( ) ) ;
//const size_t triangles_count = its.indices.size();
//if (triangles_count == 0) {
// add_error("found no trianges in the object " + std::to_string(sub_object->id));
// return false;
//}
for ( const Vec3i & face : its . indices ) {
for ( const int tri_id : face ) {
if ( tri_id < 0 | | tri_id > = int ( sub_object - > geometry . vertices . size ( ) ) ) {
add_error ( " invalid vertex id in object " + std : : to_string ( sub_object - > id ) ) ;
return false ;
}
}
}
its . vertices . assign ( sub_object - > geometry . vertices . begin ( ) , sub_object - > geometry . vertices . end ( ) ) ;
// QDS
for ( const std : : string prop_str : sub_object - > geometry . face_properties ) {
FaceProperty face_prop ;
face_prop . from_string ( prop_str ) ;
its . properties . push_back ( face_prop ) ;
}
TriangleMesh triangle_mesh ( std : : move ( its ) , volume_data - > mesh_stats ) ;
// QDS: no need to multiply the instance matrix into the volume
//if (!m_is_qdt_3mf) {
// // if the 3mf was not produced by QIDIStudio and there is only one instance,
// // bake the transformation into the geometry to allow the reload from disk command
// // to work properly
// if (object.instances.size() == 1) {
// triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
// object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
// //FIXME do the mesh fixing?
// }
//}
if ( triangle_mesh . volume ( ) < 0 )
triangle_mesh . flip_triangles ( ) ;
volume = object . add_volume ( std : : move ( triangle_mesh ) ) ;
if ( shared_mesh_id ! = - 1 )
//for some cases the shared mesh is in other plate and not loaded in cli slicing
//we need to use the first one in the same plate instead
m_shared_meshes [ shared_mesh_id ] = volume ;
else
m_shared_meshes [ sub_object - > id ] = volume ;
}
else {
//create volume to use shared mesh
volume = object . add_volume_with_shared_mesh ( * shared_volume ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : line %1%, create volume using shared_mesh %2% " ) % __LINE__ % shared_volume ;
}
// stores the volume matrix taken from the metadata, if present
if ( has_transform )
volume - > source . transform = Slic3r : : Geometry : : Transformation ( volume_matrix_to_object ) ;
volume - > calculate_convex_hull ( ) ;
//set transform from 3mf
Slic3r : : Geometry : : Transformation comp_transformatino ( sub_comp . transform ) ;
volume - > set_transformation ( comp_transformatino * volume - > get_transformation ( ) ) ;
if ( shared_volume ) {
const TriangleMesh & trangle_mesh = volume - > mesh ( ) ;
Vec3d shift = trangle_mesh . get_init_shift ( ) ;
if ( ! shift . isApprox ( Vec3d : : Zero ( ) ) )
volume - > translate ( shift ) ;
}
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
{
volume - > supported_facets . reserve ( triangles_count ) ;
volume - > seam_facets . reserve ( triangles_count ) ;
volume - > mmu_segmentation_facets . reserve ( triangles_count ) ;
for ( size_t i = 0 ; i < triangles_count ; + + i ) {
assert ( i < sub_object - > geometry . custom_supports . size ( ) ) ;
assert ( i < sub_object - > geometry . custom_seam . size ( ) ) ;
assert ( i < sub_object - > geometry . mmu_segmentation . size ( ) ) ;
if ( ! sub_object - > geometry . custom_supports [ i ] . empty ( ) )
volume - > supported_facets . set_triangle_from_string ( i , sub_object - > geometry . custom_supports [ i ] ) ;
if ( ! sub_object - > geometry . custom_seam [ i ] . empty ( ) )
volume - > seam_facets . set_triangle_from_string ( i , sub_object - > geometry . custom_seam [ i ] ) ;
if ( ! sub_object - > geometry . mmu_segmentation [ i ] . empty ( ) )
volume - > mmu_segmentation_facets . set_triangle_from_string ( i , sub_object - > geometry . mmu_segmentation [ i ] ) ;
}
volume - > supported_facets . shrink_to_fit ( ) ;
volume - > seam_facets . shrink_to_fit ( ) ;
volume - > mmu_segmentation_facets . shrink_to_fit ( ) ;
volume - > mmu_segmentation_facets . touch ( ) ;
}
volume - > set_type ( volume_data - > part_type ) ;
if ( ! volume_data - > text_info . m_text . empty ( ) )
volume - > set_text_info ( volume_data - > text_info ) ;
// apply the remaining volume's metadata
for ( const Metadata & metadata : volume_data - > metadata ) {
if ( metadata . key = = NAME_KEY )
volume - > name = metadata . value ;
//else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
// volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
//for old format
else if ( ( metadata . key = = VOLUME_TYPE_KEY ) | | ( metadata . key = = PART_TYPE_KEY ) )
volume - > set_type ( ModelVolume : : type_from_string ( metadata . value ) ) ;
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 ( ) ) ;
else if ( metadata . key = = SOURCE_IN_INCHES )
volume - > source . is_converted_from_inches = metadata . value = = " 1 " ;
else if ( metadata . key = = SOURCE_IN_METERS )
volume - > source . is_converted_from_meters = metadata . value = = " 1 " ;
else if ( ( metadata . key = = MATRIX_KEY ) | | ( metadata . key = = MESH_SHARED_KEY ) )
continue ;
else
volume - > config . set_deserialize ( metadata . key , metadata . value , config_substitutions ) ;
}
// this may happen for 3mf saved by 3rd part softwares
if ( volume - > name . empty ( ) ) {
volume - > name = object . name ;
if ( renamed_volumes_count > 0 )
volume - > name + = " _ " + std : : to_string ( renamed_volumes_count + 1 ) ;
+ + renamed_volumes_count ;
}
}
return true ;
}
bool _QDS_3MF_Importer : : _generate_volumes ( ModelObject & object , const Geometry & geometry , const ObjectMetadata : : VolumeMetadataList & volumes , ConfigSubstitutionContext & config_substitutions )
{
if ( ! object . volumes . empty ( ) ) {
add_error ( " Found invalid volumes count " ) ;
return false ;
}
unsigned int geo_tri_count = ( unsigned int ) geometry . triangles . size ( ) ;
unsigned int renamed_volumes_count = 0 ;
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 ;
}
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 ) {
if ( metadata . key = = MATRIX_KEY ) {
volume_matrix_to_object = Slic3r : : Geometry : : transform3d_from_string ( metadata . value ) ;
has_transform = ! volume_matrix_to_object . isApprox ( Transform3d : : Identity ( ) , 1e-10 ) ;
break ;
}
}
// splits volume out of imported geometry
indexed_triangle_set its ;
its . indices . assign ( geometry . triangles . begin ( ) + volume_data . first_triangle_id , geometry . triangles . begin ( ) + volume_data . last_triangle_id + 1 ) ;
const size_t triangles_count = its . indices . size ( ) ;
if ( triangles_count = = 0 ) {
add_error ( " An empty triangle mesh found " ) ;
return false ;
}
{
int min_id = its . indices . front ( ) [ 0 ] ;
int max_id = min_id ;
for ( const Vec3i & face : its . indices ) {
for ( const int tri_id : face ) {
if ( tri_id < 0 | | tri_id > = int ( geometry . vertices . size ( ) ) ) {
add_error ( " Found invalid vertex id " ) ;
return false ;
}
min_id = std : : min ( min_id , tri_id ) ;
max_id = std : : max ( max_id , tri_id ) ;
}
}
its . vertices . assign ( geometry . vertices . begin ( ) + min_id , geometry . vertices . begin ( ) + max_id + 1 ) ;
// QDS
for ( const std : : string prop_str : geometry . face_properties ) {
FaceProperty face_prop ;
face_prop . from_string ( prop_str ) ;
its . properties . push_back ( face_prop ) ;
}
// rebase indices to the current vertices list
for ( Vec3i & face : its . indices )
for ( int & tri_id : face )
tri_id - = min_id ;
}
TriangleMesh triangle_mesh ( std : : move ( its ) , volume_data . mesh_stats ) ;
if ( ! m_is_qdt_3mf ) {
// if the 3mf was not produced by QIDIStudio and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if ( object . instances . size ( ) = = 1 ) {
triangle_mesh . transform ( object . instances . front ( ) - > get_transformation ( ) . get_matrix ( ) , false ) ;
object . instances . front ( ) - > set_transformation ( Slic3r : : Geometry : : Transformation ( ) ) ;
//FIXME do the mesh fixing?
}
}
if ( triangle_mesh . volume ( ) < 0 )
triangle_mesh . flip_triangles ( ) ;
ModelVolume * volume = object . add_volume ( std : : move ( triangle_mesh ) ) ;
// stores the volume matrix taken from the metadata, if present
if ( has_transform )
volume - > source . transform = Slic3r : : Geometry : : Transformation ( volume_matrix_to_object ) ;
volume - > calculate_convex_hull ( ) ;
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
volume - > supported_facets . reserve ( triangles_count ) ;
volume - > seam_facets . reserve ( triangles_count ) ;
volume - > mmu_segmentation_facets . reserve ( triangles_count ) ;
for ( size_t i = 0 ; i < triangles_count ; + + i ) {
size_t index = volume_data . first_triangle_id + i ;
assert ( index < geometry . custom_supports . size ( ) ) ;
assert ( index < geometry . custom_seam . size ( ) ) ;
assert ( index < geometry . mmu_segmentation . size ( ) ) ;
if ( ! geometry . custom_supports [ index ] . empty ( ) )
volume - > supported_facets . set_triangle_from_string ( i , geometry . custom_supports [ index ] ) ;
if ( ! geometry . custom_seam [ index ] . empty ( ) )
volume - > seam_facets . set_triangle_from_string ( i , geometry . custom_seam [ index ] ) ;
if ( ! geometry . mmu_segmentation [ index ] . empty ( ) )
volume - > mmu_segmentation_facets . set_triangle_from_string ( i , geometry . mmu_segmentation [ index ] ) ;
}
volume - > supported_facets . shrink_to_fit ( ) ;
volume - > seam_facets . shrink_to_fit ( ) ;
volume - > mmu_segmentation_facets . shrink_to_fit ( ) ;
volume - > set_type ( volume_data . part_type ) ;
// apply the remaining volume's metadata
for ( const Metadata & metadata : volume_data . metadata ) {
if ( metadata . key = = NAME_KEY )
volume - > name = metadata . value ;
//else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
// volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
//for old format
else if ( ( metadata . key = = VOLUME_TYPE_KEY ) | | ( metadata . key = = PART_TYPE_KEY ) )
volume - > set_type ( ModelVolume : : type_from_string ( metadata . value ) ) ;
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 ( ) ) ;
else if ( metadata . key = = SOURCE_IN_INCHES )
volume - > source . is_converted_from_inches = metadata . value = = " 1 " ;
else if ( metadata . key = = SOURCE_IN_METERS )
volume - > source . is_converted_from_meters = metadata . value = = " 1 " ;
else
volume - > config . set_deserialize ( metadata . key , metadata . value , config_substitutions ) ;
}
// this may happen for 3mf saved by 3rd part softwares
if ( volume - > name . empty ( ) ) {
volume - > name = object . name ;
if ( renamed_volumes_count > 0 )
volume - > name + = " _ " + std : : to_string ( renamed_volumes_count + 1 ) ;
+ + renamed_volumes_count ;
}
}
return true ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_start_model_xml_element ( void * userData , const char * name , const char * * attributes )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_model_xml_element ( name , attributes ) ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_end_model_xml_element ( void * userData , const char * name )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_end_model_xml_element ( name ) ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_xml_characters ( void * userData , const XML_Char * s , int len )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_xml_characters ( s , len ) ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_start_config_xml_element ( void * userData , const char * name , const char * * attributes )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_start_config_xml_element ( name , attributes ) ;
}
void XMLCALL _QDS_3MF_Importer : : _handle_end_config_xml_element ( void * userData , const char * name )
{
_QDS_3MF_Importer * importer = ( _QDS_3MF_Importer * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_end_config_xml_element ( name ) ;
}
/* functions of ObjectImporter */
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_model ( const char * * attributes , unsigned int num_attributes )
{
object_unit_factor = qds_get_unit_factor ( qds_get_attribute_value_string ( attributes , num_attributes , UNIT_ATTR ) ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_model ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_resources ( const char * * attributes , unsigned int num_attributes )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_resources ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_object ( const char * * attributes , unsigned int num_attributes )
{
// reset current object data
if ( current_object ) {
delete current_object ;
current_object = nullptr ;
}
std : : string object_type = qds_get_attribute_value_string ( attributes , num_attributes , TYPE_ATTR ) ;
if ( qds_is_valid_object_type ( object_type ) ) {
if ( ! current_object ) {
current_object = new CurrentObject ( ) ;
}
current_object - > id = qds_get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
current_object - > name = qds_get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
current_object - > uuid = qds_get_attribute_value_string ( attributes , num_attributes , PUUID_ATTR ) ;
if ( current_object - > uuid . empty ( ) ) {
current_object - > uuid = qds_get_attribute_value_string ( attributes , num_attributes , PUUID_LOWER_ATTR ) ;
}
current_object - > pid = qds_get_attribute_value_int ( attributes , num_attributes , PID_ATTR ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_object ( )
{
if ( ! current_object | | ( current_object - > id = = - 1 ) ) {
top_importer - > add_error ( " Found invalid object for " + object_path ) ;
return false ;
}
else {
if ( is_qdt_3mf & & boost : : ends_with ( current_object - > uuid , OBJECT_UUID_SUFFIX ) & & top_importer - > m_load_restore ) {
std : : istringstream iss ( current_object - > uuid ) ;
int backup_id ;
bool need_replace = false ;
if ( iss > > std : : hex > > backup_id ) {
need_replace = ( current_object - > id ! = backup_id ) ;
current_object - > id = backup_id ;
}
//if (need_replace)
{
for ( int index = 0 ; index < current_object - > components . size ( ) ; index + + )
{
int temp_id = ( index + 1 ) < < 16 | backup_id ;
Component & component = current_object - > components [ index ] ;
std : : string new_path = component . object_id . first ;
Id new_id = std : : make_pair ( new_path , temp_id ) ;
IdToCurrentObjectMap : : iterator object_it = object_list . find ( component . object_id ) ;
if ( object_it ! = object_list . end ( ) ) {
CurrentObject new_object ;
new_object . geometry = std : : move ( object_it - > second . geometry ) ;
new_object . id = temp_id ;
new_object . model_object_idx = object_it - > second . model_object_idx ;
new_object . name = object_it - > second . name ;
new_object . uuid = object_it - > second . uuid ;
object_list . erase ( object_it ) ;
object_list . insert ( { new_id , std : : move ( new_object ) } ) ;
}
else {
top_importer - > add_error ( " can not find object for component, id= " + std : : to_string ( component . object_id . second ) ) ;
delete current_object ;
current_object = nullptr ;
return false ;
}
component . object_id . second = temp_id ;
}
}
}
Id id = std : : make_pair ( object_path , current_object - > id ) ;
if ( object_list . find ( id ) = = object_list . end ( ) ) {
object_list . insert ( { id , std : : move ( * current_object ) } ) ;
delete current_object ;
current_object = nullptr ;
}
else {
top_importer - > add_error ( " Found object with duplicate id for " + object_path ) ;
delete current_object ;
current_object = nullptr ;
return false ;
}
}
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_color_group ( const char * * attributes , unsigned int num_attributes )
{
object_current_color_group = qds_get_attribute_value_int ( attributes , num_attributes , ID_ATTR ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_color_group ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_color ( const char * * attributes , unsigned int num_attributes )
{
std : : string color = qds_get_attribute_value_string ( attributes , num_attributes , COLOR_ATTR ) ;
object_group_id_to_color [ object_current_color_group ] = color ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_color ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_mesh ( const char * * attributes , unsigned int num_attributes )
{
// reset current geometry
if ( current_object )
current_object - > geometry . reset ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_mesh ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_vertices ( const char * * attributes , unsigned int num_attributes )
{
// reset current vertices
if ( current_object )
current_object - > geometry . vertices . clear ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_vertices ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_vertex ( const char * * attributes , unsigned int num_attributes )
{
// appends the vertex coordinates
// missing values are set equal to ZERO
if ( current_object )
current_object - > geometry . vertices . emplace_back (
object_unit_factor * qds_get_attribute_value_float ( attributes , num_attributes , X_ATTR ) ,
object_unit_factor * qds_get_attribute_value_float ( attributes , num_attributes , Y_ATTR ) ,
object_unit_factor * qds_get_attribute_value_float ( attributes , num_attributes , Z_ATTR ) ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_vertex ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_triangles ( const char * * attributes , unsigned int num_attributes )
{
// reset current triangles
if ( current_object )
current_object - > geometry . triangles . clear ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_triangles ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_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
if ( current_object ) {
current_object - > geometry . triangles . emplace_back (
qds_get_attribute_value_int ( attributes , num_attributes , V1_ATTR ) ,
qds_get_attribute_value_int ( attributes , num_attributes , V2_ATTR ) ,
qds_get_attribute_value_int ( attributes , num_attributes , V3_ATTR ) ) ;
current_object - > geometry . custom_supports . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , CUSTOM_SUPPORTS_ATTR ) ) ;
current_object - > geometry . custom_seam . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , CUSTOM_SEAM_ATTR ) ) ;
current_object - > geometry . mmu_segmentation . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , MMU_SEGMENTATION_ATTR ) ) ;
// QDS
current_object - > geometry . face_properties . push_back ( qds_get_attribute_value_string ( attributes , num_attributes , FACE_PROPERTY_ATTR ) ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_triangle ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_components ( const char * * attributes , unsigned int num_attributes )
{
// reset current components
if ( current_object )
current_object - > components . clear ( ) ;
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_components ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_component ( const char * * attributes , unsigned int num_attributes )
{
int object_id = qds_get_attribute_value_int ( attributes , num_attributes , OBJECTID_ATTR ) ;
Transform3d transform = qds_get_transform_from_3mf_specs_string ( qds_get_attribute_value_string ( attributes , num_attributes , TRANSFORM_ATTR ) ) ;
/*Id id = std::make_pair(m_sub_model_path, object_id);
IdToModelObjectMap : : iterator object_item = m_objects . find ( id ) ;
if ( object_item = = m_objects . end ( ) ) {
IdToAliasesMap : : iterator alias_item = m_objects_aliases . find ( id ) ;
if ( alias_item = = m_objects_aliases . end ( ) ) {
add_error ( " Found component with invalid object id " ) ;
return false ;
}
} */
if ( current_object ) {
Id id = std : : make_pair ( object_path , object_id ) ;
current_object - > components . emplace_back ( id , transform ) ;
}
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_component ( )
{
// do nothing
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_metadata ( const char * * attributes , unsigned int num_attributes )
{
obj_curr_metadata_name . clear ( ) ;
std : : string name = qds_get_attribute_value_string ( attributes , num_attributes , NAME_ATTR ) ;
if ( ! name . empty ( ) ) {
obj_curr_metadata_name = name ;
}
return true ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_metadata ( )
{
if ( ( obj_curr_metadata_name = = QDS_3MF_VERSION ) | | ( obj_curr_metadata_name = = QDS_3MF_VERSION1 ) ) {
is_qdt_3mf = true ;
}
return true ;
}
void _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_model_xml_element ( const char * name , const char * * attributes )
{
if ( object_xml_parser = = nullptr )
return ;
bool res = true ;
unsigned int num_attributes = ( unsigned int ) XML_GetSpecifiedAttributeCount ( object_xml_parser ) ;
if ( : : strcmp ( MODEL_TAG , name ) = = 0 )
res = _handle_object_start_model ( attributes , num_attributes ) ;
else if ( : : strcmp ( RESOURCES_TAG , name ) = = 0 )
res = _handle_object_start_resources ( attributes , num_attributes ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_object_start_object ( attributes , num_attributes ) ;
else if ( : : strcmp ( COLOR_GROUP_TAG , name ) = = 0 )
res = _handle_object_start_color_group ( attributes , num_attributes ) ;
else if ( : : strcmp ( COLOR_TAG , name ) = = 0 )
res = _handle_object_start_color ( attributes , num_attributes ) ;
else if ( : : strcmp ( MESH_TAG , name ) = = 0 )
res = _handle_object_start_mesh ( attributes , num_attributes ) ;
else if ( : : strcmp ( VERTICES_TAG , name ) = = 0 )
res = _handle_object_start_vertices ( attributes , num_attributes ) ;
else if ( : : strcmp ( VERTEX_TAG , name ) = = 0 )
res = _handle_object_start_vertex ( attributes , num_attributes ) ;
else if ( : : strcmp ( TRIANGLES_TAG , name ) = = 0 )
res = _handle_object_start_triangles ( attributes , num_attributes ) ;
else if ( : : strcmp ( TRIANGLE_TAG , name ) = = 0 )
res = _handle_object_start_triangle ( attributes , num_attributes ) ;
else if ( : : strcmp ( COMPONENTS_TAG , name ) = = 0 )
res = _handle_object_start_components ( attributes , num_attributes ) ;
else if ( : : strcmp ( COMPONENT_TAG , name ) = = 0 )
res = _handle_object_start_component ( attributes , num_attributes ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_object_start_metadata ( attributes , num_attributes ) ;
if ( ! res )
_stop_object_xml_parser ( ) ;
}
void _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_model_xml_element ( const char * name )
{
if ( object_xml_parser = = nullptr )
return ;
bool res = true ;
if ( : : strcmp ( MODEL_TAG , name ) = = 0 )
res = _handle_object_end_model ( ) ;
else if ( : : strcmp ( RESOURCES_TAG , name ) = = 0 )
res = _handle_object_end_resources ( ) ;
else if ( : : strcmp ( OBJECT_TAG , name ) = = 0 )
res = _handle_object_end_object ( ) ;
else if ( : : strcmp ( COLOR_GROUP_TAG , name ) = = 0 )
res = _handle_object_end_color_group ( ) ;
else if ( : : strcmp ( COLOR_TAG , name ) = = 0 )
res = _handle_object_end_color ( ) ;
else if ( : : strcmp ( MESH_TAG , name ) = = 0 )
res = _handle_object_end_mesh ( ) ;
else if ( : : strcmp ( VERTICES_TAG , name ) = = 0 )
res = _handle_object_end_vertices ( ) ;
else if ( : : strcmp ( VERTEX_TAG , name ) = = 0 )
res = _handle_object_end_vertex ( ) ;
else if ( : : strcmp ( TRIANGLES_TAG , name ) = = 0 )
res = _handle_object_end_triangles ( ) ;
else if ( : : strcmp ( TRIANGLE_TAG , name ) = = 0 )
res = _handle_object_end_triangle ( ) ;
else if ( : : strcmp ( COMPONENTS_TAG , name ) = = 0 )
res = _handle_object_end_components ( ) ;
else if ( : : strcmp ( COMPONENT_TAG , name ) = = 0 )
res = _handle_object_end_component ( ) ;
else if ( : : strcmp ( METADATA_TAG , name ) = = 0 )
res = _handle_object_end_metadata ( ) ;
if ( ! res )
_stop_object_xml_parser ( ) ;
}
void _QDS_3MF_Importer : : ObjectImporter : : _handle_object_xml_characters ( const XML_Char * s , int len )
{
obj_curr_characters . append ( s , len ) ;
}
void XMLCALL _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_model_xml_element ( void * userData , const char * name , const char * * attributes )
{
ObjectImporter * importer = ( ObjectImporter * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_object_start_model_xml_element ( name , attributes ) ;
}
void XMLCALL _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_model_xml_element ( void * userData , const char * name )
{
ObjectImporter * importer = ( ObjectImporter * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_object_end_model_xml_element ( name ) ;
}
void XMLCALL _QDS_3MF_Importer : : ObjectImporter : : _handle_object_xml_characters ( void * userData , const XML_Char * s , int len )
{
ObjectImporter * importer = ( ObjectImporter * ) userData ;
if ( importer ! = nullptr )
importer - > _handle_object_xml_characters ( s , len ) ;
}
bool _QDS_3MF_Importer : : ObjectImporter : : _extract_object_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat )
{
if ( stat . m_uncomp_size = = 0 ) {
top_importer - > add_error ( " Found invalid size for " + object_path ) ;
return false ;
}
object_xml_parser = XML_ParserCreate ( nullptr ) ;
if ( object_xml_parser = = nullptr ) {
top_importer - > add_error ( " Unable to create parser for " + object_path ) ;
return false ;
}
XML_SetUserData ( object_xml_parser , ( void * ) this ) ;
XML_SetElementHandler ( object_xml_parser , _QDS_3MF_Importer : : ObjectImporter : : _handle_object_start_model_xml_element , _QDS_3MF_Importer : : ObjectImporter : : _handle_object_end_model_xml_element ) ;
XML_SetCharacterDataHandler ( object_xml_parser , _QDS_3MF_Importer : : ObjectImporter : : _handle_object_xml_characters ) ;
struct CallbackData
{
XML_Parser & parser ;
_QDS_3MF_Importer : : ObjectImporter & importer ;
const mz_zip_archive_file_stat & stat ;
CallbackData ( XML_Parser & parser , _QDS_3MF_Importer : : ObjectImporter & importer , const mz_zip_archive_file_stat & stat ) : parser ( parser ) , importer ( importer ) , stat ( stat ) { }
} ;
CallbackData data ( object_xml_parser , * this , stat ) ;
mz_bool res = 0 ;
try
{
mz_file_write_func callback = [ ] ( 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 ) | | data - > importer . object_parse_error ( ) ) {
char error_buf [ 1024 ] ;
: : snprintf ( error_buf , 1024 , " Error (%s) while parsing '%s' at line %d " , data - > importer . object_parse_error_message ( ) , data - > stat . m_filename , ( int ) XML_GetCurrentLineNumber ( data - > parser ) ) ;
throw Slic3r : : FileIOError ( error_buf ) ;
}
return n ;
} ;
void * opaque = & data ;
res = mz_zip_reader_extract_to_callback ( & archive , stat . m_file_index , callback , opaque , 0 ) ;
}
catch ( const version_error & e )
{
// rethrow the exception
std : : string error_message = std : : string ( e . what ( ) ) + " for " + object_path ;
throw Slic3r : : FileIOError ( error_message ) ;
}
catch ( std : : exception & e )
{
std : : string error_message = std : : string ( e . what ( ) ) + " for " + object_path ;
top_importer - > add_error ( error_message ) ;
return false ;
}
if ( res = = 0 ) {
top_importer - > add_error ( " Error while extracting model data from zip archive for " + object_path ) ;
return false ;
}
return true ;
}
class _QDS_3MF_Exporter : public _QDS_3MF_Base
{
struct BuildItem
{
std : : string path ;
unsigned int id ;
Transform3d transform ;
bool printable ;
BuildItem ( std : : string const & path , unsigned int id , const Transform3d & transform , const bool printable )
: path ( path )
, id ( id )
, transform ( transform )
, printable ( printable )
{
}
} ;
//QDS: change volume to seperate objects
/*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;
typedef std : : map < const ModelVolume * , int > VolumeToObjectIDMap ;
struct ObjectData
{
ModelObject const * object ;
int backup_id ;
int object_id = 0 ;
std : : string sub_path ;
bool share_mesh = false ;
VolumeToObjectIDMap volumes_objectID ;
} ;
typedef std : : vector < BuildItem > BuildItemsList ;
typedef std : : map < ModelObject const * , ObjectData > ObjectToObjectDataMap ;
bool m_fullpath_sources { true } ;
bool m_zip64 { true } ;
bool m_production_ext { false } ; // save with Production Extention
bool m_skip_static { false } ; // not save mesh and other big static contents
bool m_from_backup_save { false } ; // the object save is from backup store
bool m_split_model { false } ; // save object per file with Production Extention
bool m_save_gcode { false } ; // whether to save gcode for normal save
bool m_skip_model { false } ; // skip model when exporting .gcode.3mf
bool m_skip_auxiliary { false } ; // skip normal axuiliary files
bool m_use_loaded_id { false } ; // whether to use loaded id for identify_id
bool m_share_mesh { false } ; // whether to share mesh between objects
std : : string m_thumbnail_middle = PRINTER_THUMBNAIL_MIDDLE_FILE ;
std : : string m_thumbnail_small = PRINTER_THUMBNAIL_SMALL_FILE ;
std : : map < void const * , std : : pair < ObjectData * , ModelVolume const * > > m_shared_meshes ;
std : : map < ModelVolume const * , std : : pair < std : : string , int > > m_volume_paths ;
public :
//QDS: add plate data related logic
// add backup logic
//bool save_model_to_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, const DynamicPrintConfig* config, bool fullpath_sources, const std::vector<ThumbnailData*>& thumbnail_data, bool zip64, bool skip_static, Export3mfProgressFn proFn = nullptr, bool silence = false);
bool save_model_to_file ( StoreParams & store_params ) ;
// add backup logic
bool save_object_mesh ( const std : : string & temp_path , ModelObject const & object , int obj_id ) ;
private :
//QDS: add plate data related logic
bool _save_model_to_file ( const std : : string & filename ,
Model & model , PlateDataPtrs & plate_data_list ,
std : : vector < Preset * > & project_presets ,
const DynamicPrintConfig * config ,
const std : : vector < ThumbnailData * > & thumbnail_data ,
const std : : vector < ThumbnailData * > & no_light_thumbnail_data ,
const std : : vector < ThumbnailData * > & top_thumbnail_data ,
const std : : vector < ThumbnailData * > & pick_thumbnail_data ,
Export3mfProgressFn proFn ,
const std : : vector < ThumbnailData * > & calibration_data ,
const std : : vector < PlateBBoxData * > & id_bboxes ,
QDTProject * project = nullptr ,
int export_plate_idx = - 1 ) ;
bool _add_file_to_archive ( mz_zip_archive & archive , const std : : string & path_in_zip , const std : : string & file_path ) ;
bool _add_content_types_file_to_archive ( mz_zip_archive & archive ) ;
bool _add_thumbnail_file_to_archive ( mz_zip_archive & archive , const ThumbnailData & thumbnail_data , const char * local_path , int index , bool generate_small_thumbnail = false ) ;
bool _add_calibration_file_to_archive ( mz_zip_archive & archive , const ThumbnailData & thumbnail_data , int index ) ;
bool _add_bbox_file_to_archive ( mz_zip_archive & archive , const PlateBBoxData & id_bboxes , int index ) ;
bool _add_relationships_file_to_archive ( mz_zip_archive & archive ,
std : : string const & from = { } ,
std : : vector < std : : string > const & targets = { } ,
std : : vector < std : : string > const & types = { } ,
PackingTemporaryData data = PackingTemporaryData ( ) ,
int export_plate_idx = - 1 ) const ;
bool _add_model_file_to_archive ( const std : : string & filename , mz_zip_archive & archive , const Model & model , ObjectToObjectDataMap & objects_data , Export3mfProgressFn proFn = nullptr , QDTProject * project = nullptr ) const ;
bool _add_object_to_model_stream ( mz_zip_writer_staged_context & context , ObjectData const & object_data ) const ;
void _add_object_components_to_stream ( std : : stringstream & stream , ObjectData const & object_data ) const ;
//QDS: change volume to seperate objects
bool _add_mesh_to_object_stream ( std : : function < bool ( std : : string & , bool ) > const & flush , ObjectData const & object_data ) const ;
bool _add_build_to_model_stream ( std : : stringstream & stream , const BuildItemsList & build_items ) const ;
bool _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
bool _add_layer_config_ranges_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
bool _add_sla_support_points_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
bool _add_sla_drain_holes_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
bool _add_print_config_file_to_archive ( mz_zip_archive & archive , const DynamicPrintConfig & config ) ;
//QDS: add project config file logic for json format
bool _add_project_config_file_to_archive ( mz_zip_archive & archive , const DynamicPrintConfig & config , Model & model ) ;
//QDS: add project embedded preset files
bool _add_project_embedded_presets_to_archive ( mz_zip_archive & archive , Model & model , std : : vector < Preset * > project_presets ) ;
bool _add_model_config_file_to_archive ( mz_zip_archive & archive , const Model & model , PlateDataPtrs & plate_data_list , const ObjectToObjectDataMap & objects_data , int export_plate_idx = - 1 , bool save_gcode = true , bool use_loaded_id = false ) ;
bool _add_cut_information_file_to_archive ( mz_zip_archive & archive , Model & model ) ;
bool _add_slice_info_config_file_to_archive ( mz_zip_archive & archive , const Model & model , PlateDataPtrs & plate_data_list , const ObjectToObjectDataMap & objects_data , const DynamicPrintConfig & config ) ;
bool _add_gcode_file_to_archive ( mz_zip_archive & archive , const Model & model , PlateDataPtrs & plate_data_list , Export3mfProgressFn proFn = nullptr ) ;
bool _add_custom_gcode_per_print_z_file_to_archive ( mz_zip_archive & archive , Model & model , const DynamicPrintConfig * config ) ;
bool _add_auxiliary_dir_to_archive ( mz_zip_archive & archive , const std : : string & aux_dir , PackingTemporaryData & data ) ;
static int convert_instance_id_to_resource_id ( const Model & model , int obj_id , int instance_id )
{
int resource_id = 1 ;
for ( int i = 0 ; i < obj_id ; + + i )
{
resource_id + = model . objects [ i ] - > volumes . size ( ) + 1 ;
}
resource_id + = model . objects [ obj_id ] - > volumes . size ( ) ;
return resource_id ;
}
} ;
bool _QDS_3MF_Exporter : : save_model_to_file ( StoreParams & store_params )
{
clear_errors ( ) ;
m_fullpath_sources = store_params . strategy & SaveStrategy : : FullPathSources ;
m_zip64 = store_params . strategy & SaveStrategy : : Zip64 ;
m_production_ext = store_params . strategy & SaveStrategy : : ProductionExt ;
m_skip_static = store_params . strategy & SaveStrategy : : SkipStatic ;
m_split_model = store_params . strategy & SaveStrategy : : SplitModel ;
m_save_gcode = store_params . strategy & SaveStrategy : : WithGcode ;
m_skip_model = store_params . strategy & SaveStrategy : : SkipModel ;
m_skip_auxiliary = store_params . strategy & SaveStrategy : : SkipAuxiliary ;
m_share_mesh = store_params . strategy & SaveStrategy : : ShareMesh ;
m_from_backup_save = store_params . strategy & SaveStrategy : : Backup ;
m_use_loaded_id = store_params . strategy & SaveStrategy : : UseLoadedId ;
if ( auto info = store_params . model - > model_info ) {
if ( auto iter = info - > metadata_items . find ( " Thumbnail_Small " ) ; iter ! = info - > metadata_items . end ( ) )
m_thumbnail_small = iter - > second ;
if ( auto iter = info - > metadata_items . find ( " Thumbnail_Middle " ) ; iter ! = info - > metadata_items . end ( ) )
m_thumbnail_middle = iter - > second ;
}
boost : : system : : error_code ec ;
std : : string filename = std : : string ( store_params . path ) ;
boost : : filesystem : : remove ( filename + " .tmp " , ec ) ;
bool result = _save_model_to_file ( filename + " .tmp " , * store_params . model , store_params . plate_data_list , store_params . project_presets , store_params . config ,
store_params . thumbnail_data , store_params . no_light_thumbnail_data , store_params . top_thumbnail_data , store_params . pick_thumbnail_data ,
store_params . proFn ,
store_params . calibration_thumbnail_data , store_params . id_bboxes , store_params . project , store_params . export_plate_idx ) ;
if ( result ) {
boost : : filesystem : : rename ( filename + " .tmp " , filename , ec ) ;
if ( ec ) {
add_error ( " Failed to rename file: " + ec . message ( ) ) ;
boost : : filesystem : : remove ( filename + " .tmp " , ec ) ;
return false ;
}
if ( ! ( store_params . strategy & SaveStrategy : : Silence ) )
boost : : filesystem : : save_string_file ( store_params . model - > get_backup_path ( ) + " /origin.txt " , filename ) ;
}
return result ;
}
// backup mesh-only
bool _QDS_3MF_Exporter : : save_object_mesh ( const std : : string & temp_path , ModelObject const & object , int obj_id )
{
m_production_ext = true ;
m_from_backup_save = true ;
Model const & model = * object . get_model ( ) ;
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
auto filename = boost : : format ( " 3D/Objects/object_%d.model " ) % obj_id ;
std : : string filepath = temp_path + " / " + filename . str ( ) ;
std : : string filepath_tmp = filepath + " .tmp " ;
boost : : system : : error_code ec ;
boost : : filesystem : : remove ( filepath_tmp , ec ) ;
if ( ! open_zip_writer ( & archive , filepath_tmp ) ) {
add_error ( " Unable to open the file " + filepath_tmp ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to open the file \n " ) ;
return false ;
}
struct close_lock
{
mz_zip_archive & archive ;
std : : string const * filename ;
void close ( ) {
close_zip_writer ( & archive ) ;
filename = nullptr ;
}
~ close_lock ( ) {
if ( filename ) {
close_zip_writer ( & archive ) ;
boost : : system : : error_code ec ;
boost : : filesystem : : remove ( * filename , ec ) ;
}
}
} lock { archive , & filepath_tmp } ;
ObjectToObjectDataMap objects_data ;
auto & volumes_objectID = objects_data . insert ( { & object , { & object , obj_id } } ) . first - > second . volumes_objectID ;
unsigned int volume_count = 0 ;
for ( ModelVolume * volume : object . volumes ) {
if ( volume = = nullptr ) continue ;
volumes_objectID . insert ( { volume , ( + + volume_count < < 16 | obj_id ) } ) ;
}
_add_model_file_to_archive ( filename . str ( ) , archive , model , objects_data ) ;
mz_zip_writer_finalize_archive ( & archive ) ;
lock . close ( ) ;
boost : : filesystem : : rename ( filepath_tmp , filepath , ec ) ;
return true ;
}
//QDS: add plate data related logic
bool _QDS_3MF_Exporter : : _save_model_to_file ( const std : : string & filename ,
Model & model ,
PlateDataPtrs & plate_data_list ,
std : : vector < Preset * > & project_presets ,
const DynamicPrintConfig * config ,
const std : : vector < ThumbnailData * > & thumbnail_data ,
const std : : vector < ThumbnailData * > & no_light_thumbnail_data ,
const std : : vector < ThumbnailData * > & top_thumbnail_data ,
const std : : vector < ThumbnailData * > & pick_thumbnail_data ,
Export3mfProgressFn proFn ,
const std : : vector < ThumbnailData * > & calibration_data ,
const std : : vector < PlateBBoxData * > & id_bboxes ,
QDTProject * project ,
int export_plate_idx )
{
PackingTemporaryData temp_data ;
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
bool cb_cancel = false ;
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < <
boost : : format ( " ,before open zip writer, m_skip_static %1%, m_save_gcode %2%, m_use_loaded_id %3% " ) % m_skip_static % m_save_gcode % m_use_loaded_id ;
if ( proFn ) {
proFn ( EXPORT_STAGE_OPEN_3MF , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
if ( ! open_zip_writer ( & archive , filename ) ) {
add_error ( " Unable to open the file " + filename ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to open the file \n " ) ;
return false ;
}
struct close_lock
{
mz_zip_archive & archive ;
std : : string const * filename ;
~ close_lock ( ) {
close_zip_writer ( & archive ) ;
if ( filename ) {
boost : : system : : error_code ec ;
boost : : filesystem : : remove ( * filename , ec ) ;
}
}
} lock { archive , & filename } ;
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add _add_content_types_file_to_archive \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_CONTENT_TYPES , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
// Adds content types file ("[Content_Types].xml";).
// The content of this file is the same for each QIDIStudio 3mf.
if ( ! _add_content_types_file_to_archive ( archive ) ) {
return false ;
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,before add thumbnails, count %1% " ) % thumbnail_data . size ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,top&&pick thumbnails, count %1% " ) % top_thumbnail_data . size ( ) ;
//QDS: add thumbnail for each plate
if ( ! m_skip_static ) {
std : : vector < bool > thumbnail_status ( plate_data_list . size ( ) , false ) ;
std : : vector < bool > no_light_thumbnail_status ( plate_data_list . size ( ) , false ) ;
std : : vector < bool > top_thumbnail_status ( plate_data_list . size ( ) , false ) ;
std : : vector < bool > pick_thumbnail_status ( plate_data_list . size ( ) , false ) ;
if ( ( thumbnail_data . size ( ) > 0 ) & & ( thumbnail_data . size ( ) > plate_data_list . size ( ) ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , thumbnail_data size %1% > plate count %2% " )
% thumbnail_data . size ( ) % plate_data_list . size ( ) ;
return false ;
}
if ( ( no_light_thumbnail_data . size ( ) > 0 ) & & ( no_light_thumbnail_data . size ( ) > plate_data_list . size ( ) ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , no_light_thumbnail_data size %1% > plate count %2% " ) %
no_light_thumbnail_data . size ( ) % plate_data_list . size ( ) ;
return false ;
}
if ( ( top_thumbnail_data . size ( ) > 0 ) & & ( top_thumbnail_data . size ( ) > plate_data_list . size ( ) ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , top_thumbnail_data size %1% > plate count %2% " )
% top_thumbnail_data . size ( ) % plate_data_list . size ( ) ;
return false ;
}
if ( ( pick_thumbnail_data . size ( ) > 0 ) & & ( pick_thumbnail_data . size ( ) > plate_data_list . size ( ) ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , pick_thumbnail_data size %1% > plate count %2% " )
% pick_thumbnail_data . size ( ) % plate_data_list . size ( ) ;
return false ;
}
if ( top_thumbnail_data . size ( ) ! = pick_thumbnail_data . size ( ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , top_thumbnail_data size %1% != pick_thumbnail_data size %2% " )
% top_thumbnail_data . size ( ) % pick_thumbnail_data . size ( ) ;
return false ;
}
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_THUMBNAILS , 0 , plate_data_list . size ( ) , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
for ( unsigned int index = 0 ; index < thumbnail_data . size ( ) ; index + + )
{
if ( thumbnail_data [ index ] - > is_valid ( ) )
{
if ( ! _add_thumbnail_file_to_archive ( archive , * thumbnail_data [ index ] , " Metadata/plate " , index , true ) ) {
return false ;
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,add thumbnail %1%'s data into 3mf " ) % ( index + 1 ) ;
thumbnail_status [ index ] = true ;
}
}
for ( unsigned int index = 0 ; index < no_light_thumbnail_data . size ( ) ; index + + ) {
if ( no_light_thumbnail_data [ index ] - > is_valid ( ) ) {
if ( ! _add_thumbnail_file_to_archive ( archive , * no_light_thumbnail_data [ index ] , " Metadata/plate_no_light " , index ) ) {
return false ;
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,add no light thumbnail %1%'s data into 3mf " ) % ( index + 1 ) ;
no_light_thumbnail_status [ index ] = true ;
}
}
// Adds the file Metadata/top_i.png and Metadata/pick_i.png
for ( unsigned int index = 0 ; index < top_thumbnail_data . size ( ) ; index + + )
{
if ( top_thumbnail_data [ index ] - > is_valid ( ) )
{
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,add top thumbnail %1%'s data into 3mf " ) % ( index + 1 ) ;
if ( ! _add_thumbnail_file_to_archive ( archive , * top_thumbnail_data [ index ] , " Metadata/top " , index ) ) {
return false ;
}
top_thumbnail_status [ index ] = true ;
}
if ( pick_thumbnail_data [ index ] - > is_valid ( ) )
{
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,add pick thumbnail %1%'s data into 3mf " ) % ( index + 1 ) ;
if ( ! _add_thumbnail_file_to_archive ( archive , * pick_thumbnail_data [ index ] , " Metadata/pick " , index ) ) {
return false ;
}
pick_thumbnail_status [ index ] = true ;
}
}
for ( int i = 0 ; i < plate_data_list . size ( ) ; i + + ) {
PlateData * plate_data = plate_data_list [ i ] ;
if ( ! thumbnail_status [ i ] & & ! plate_data - > thumbnail_file . empty ( ) & & ( boost : : filesystem : : exists ( plate_data - > thumbnail_file ) ) ) {
std : : string dst_in_3mf = ( boost : : format ( " Metadata/plate_%1%.png " ) % ( i + 1 ) ) . str ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add thumbnail %1% from file %2% " ) % ( i + 1 ) % plate_data - > thumbnail_file ;
if ( ! _add_file_to_archive ( archive , dst_in_3mf , plate_data - > thumbnail_file ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add thumbnail %1% from file %2% failed \n " ) % ( i + 1 ) % plate_data - > thumbnail_file ;
return false ;
}
}
if ( ! no_light_thumbnail_status [ i ] & & ! plate_data - > no_light_thumbnail_file . empty ( ) & & ( boost : : filesystem : : exists ( plate_data - > no_light_thumbnail_file ) ) ) {
std : : string dst_in_3mf = ( boost : : format ( " Metadata/plate_no_light_%1%.png " ) % ( i + 1 ) ) . str ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add no light thumbnail %1% from file %2% " ) % ( i + 1 ) % plate_data - > no_light_thumbnail_file ;
if ( ! _add_file_to_archive ( archive , dst_in_3mf , plate_data - > no_light_thumbnail_file ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add no light thumbnail %1% from file %2% failed \n " ) % ( i + 1 ) % plate_data - > no_light_thumbnail_file ;
return false ;
}
}
if ( ! top_thumbnail_status [ i ] & & ! plate_data - > top_file . empty ( ) & & ( boost : : filesystem : : exists ( plate_data - > top_file ) ) ) {
std : : string dst_in_3mf = ( boost : : format ( " Metadata/top_%1%.png " ) % ( i + 1 ) ) . str ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add top thumbnail %1% from file %2% " ) % ( i + 1 ) % plate_data - > top_file ;
if ( ! _add_file_to_archive ( archive , dst_in_3mf , plate_data - > top_file ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add top thumbnail %1% failed " ) % ( i + 1 ) ;
return false ;
}
top_thumbnail_status [ i ] = true ;
}
if ( ! pick_thumbnail_status [ i ] & & ! plate_data - > pick_file . empty ( ) & & ( boost : : filesystem : : exists ( plate_data - > pick_file ) ) ) {
std : : string dst_in_3mf = ( boost : : format ( " Metadata/pick_%1%.png " ) % ( i + 1 ) ) . str ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add pick thumbnail %1% from file %2% " ) % ( i + 1 ) % plate_data - > pick_file ;
if ( ! _add_file_to_archive ( archive , dst_in_3mf , plate_data - > pick_file ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add pick thumbnail %1% failed " ) % ( i + 1 ) ;
return false ;
}
pick_thumbnail_status [ i ] = true ;
}
}
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_THUMBNAILS , plate_data_list . size ( ) , plate_data_list . size ( ) , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,before add calibration thumbnails, count %1% \n " ) % calibration_data . size ( ) ;
//QDS add calibration thumbnail for each plate
if ( ! m_skip_static & & calibration_data . size ( ) > 0 ) {
// Adds the file Metadata/calibration_p[X].png.
for ( unsigned int index = 0 ; index < calibration_data . size ( ) ; index + + )
{
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_THUMBNAILS , index , calibration_data . size ( ) , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
if ( calibration_data [ index ] - > is_valid ( ) )
{
if ( ! _add_calibration_file_to_archive ( archive , * calibration_data [ index ] , index ) ) {
close_zip_writer ( & archive ) ;
return false ;
}
}
}
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " ,before add calibration boundingbox, count %1% \n " ) % id_bboxes . size ( ) ;
if ( ! m_skip_static & & id_bboxes . size ( ) > 0 ) {
// Adds the file Metadata/calibration_p[X].png.
for ( unsigned int index = 0 ; index < id_bboxes . size ( ) ; index + + )
{
// QDS: save bounding box to json
if ( id_bboxes [ index ] - > is_valid ( ) ) {
if ( ! _add_bbox_file_to_archive ( archive , * id_bboxes [ index ] , index ) ) {
close_zip_writer ( & archive ) ;
return false ;
}
}
}
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add models \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_MODELS , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
// Adds model file ("3D/3dmodel.model").
// This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
ObjectToObjectDataMap objects_data ;
//if (!m_skip_model)
{
if ( ! _add_model_file_to_archive ( filename , archive , model , objects_data , proFn , project ) ) { return false ; }
// 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!
if ( ! _add_layer_height_profile_file_to_archive ( archive , model ) ) {
close_zip_writer ( & archive ) ;
return false ;
}
// QDS progress point
/*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_LAYER_RANGE\n");
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_LAYER_RANGE , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
} */
// 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 ) ;
return false ;
}
// QDS progress point
/*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_SUPPORT\n");
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_SUPPORT , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
// 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!
if ( ! _add_sla_support_points_file_to_archive ( archive , model ) ) {
return false ;
}
if ( ! _add_sla_drain_holes_file_to_archive ( archive , model ) ) {
return false ;
} */
// QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add custom gcodes \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_CUSTOM_GCODE , 0 , 1 , cb_cancel ) ;
if ( cb_cancel ) return false ;
}
// Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
// All custom gcode per height of whole Model are stored here
if ( ! _add_custom_gcode_per_print_z_file_to_archive ( archive , model , config ) ) { return false ; }
// QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add project_settings \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_PRINT_CONFIG , 0 , 1 , cb_cancel ) ;
if ( cb_cancel ) return false ;
}
// Adds slic3r print config file ("Metadata/Slic3r_PE.config").
// This file contains the content of FullPrintConfing / SLAFullPrintConfig.
if ( config ! = nullptr ) {
// QDS: change to json format
// if (!_add_print_config_file_to_archive(archive, *config)) {
if ( ! _add_project_config_file_to_archive ( archive , * config , model ) ) { return false ; }
}
// QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add project embedded settings \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_CONFIG_FILE , 0 , 1 , cb_cancel ) ;
if ( cb_cancel ) return false ;
}
// QDS: add project config
if ( project_presets . size ( ) > 0 ) {
// QDS: add project embedded preset files
_add_project_embedded_presets_to_archive ( archive , model , project_presets ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , finished add project embedded settings, size %1% \n " ) % project_presets . size ( ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_PROJECT_CONFIG , 0 , 1 , cb_cancel ) ;
if ( cb_cancel ) return false ;
}
}
}
// add plate_N.gcode.md5 to file
if ( ! m_skip_static & & m_save_gcode ) {
for ( int i = 0 ; i < plate_data_list . size ( ) ; i + + ) {
PlateData * plate_data = plate_data_list [ i ] ;
if ( ! plate_data - > gcode_file . empty ( ) & & plate_data - > is_sliced_valid & & boost : : filesystem : : exists ( plate_data - > gcode_file ) ) {
unsigned char digest [ 16 ] ;
MD5_CTX ctx ;
MD5_Init ( & ctx ) ;
auto src_gcode_file = plate_data - > gcode_file ;
boost : : filesystem : : ifstream ifs ( src_gcode_file , std : : ios : : binary ) ;
std : : string buf ( 64 * 1024 , 0 ) ;
const std : : size_t & size = boost : : filesystem : : file_size ( src_gcode_file ) ;
std : : size_t left_size = size ;
while ( ifs ) {
ifs . read ( buf . data ( ) , buf . size ( ) ) ;
int read_bytes = ifs . gcount ( ) ;
MD5_Update ( & ctx , ( unsigned char * ) buf . data ( ) , read_bytes ) ;
}
MD5_Final ( digest , & ctx ) ;
char md5_str [ 33 ] ;
for ( int j = 0 ; j < 16 ; j + + ) { sprintf ( & md5_str [ j * 2 ] , " %02X " , ( unsigned int ) digest [ j ] ) ; }
plate_data - > gcode_file_md5 = std : : string ( md5_str ) ;
std : : string target_file = ( boost : : format ( " Metadata/plate_%1%.gcode.md5 " ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , target_file . c_str ( ) , ( const void * ) plate_data - > gcode_file_md5 . c_str ( ) , plate_data - > gcode_file_md5 . length ( ) ,
MZ_DEFAULT_COMPRESSION ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__
< < boost : : format ( " , store gcode md5 to 3mf's %1%, length %2%, failed \n " ) % target_file % plate_data - > gcode_file_md5 . length ( ) ;
return false ;
}
}
}
}
// Adds gcode files ("Metadata/plate_1.gcode, plate_2.gcode, ...)
// Before _add_model_config_file_to_archive, because we modify plate_data
//if (!m_skip_static && !_add_gcode_file_to_archive(archive, model, plate_data_list, proFn)) {
if ( ! m_skip_static & & m_save_gcode & & ! _add_gcode_file_to_archive ( archive , model , plate_data_list , proFn ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , _add_gcode_file_to_archive failed \n " ) ;
return false ;
}
// 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 , plate_data_list , objects_data , export_plate_idx , m_save_gcode , m_use_loaded_id ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , _add_model_config_file_to_archive failed \n " ) ;
return false ;
}
if ( ! _add_cut_information_file_to_archive ( archive , model ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , _add_cut_information_file_to_archive failed \n " ) ;
return false ;
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add sliced info to 3mf \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_SLICE_INFO , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
// Adds sliced info of plate file ("Metadata/slice_info.config")
// This file contains all sliced info of all plates
if ( ! _add_slice_info_config_file_to_archive ( archive , model , plate_data_list , objects_data , * config ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , _add_slice_info_config_file_to_archive failed \n " ) ;
return false ;
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add auxiliary dir to 3mf \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_AUXILIARIES , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
if ( ! m_skip_static & & ! _add_auxiliary_dir_to_archive ( archive , model . get_auxiliary_file_temp_path ( ) , temp_data ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , _add_auxiliary_dir_to_archive failed \n " ) ;
return false ;
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , before add relation file to 3mf \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_RELATIONS , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
// Adds relationships file ("_rels/.rels").
// The content of this file is the same for each QIDIStudio 3mf.
// The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
if ( ! _add_relationships_file_to_archive ( archive , { } , { } , { } , temp_data , export_plate_idx ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , _add_relationships_file_to_archive failed \n " ) ;
return false ;
}
if ( ! mz_zip_writer_finalize_archive ( & archive ) ) {
add_error ( " Unable to finalize the archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to finalize the archive \n " ) ;
return false ;
}
//QDS progress point
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , finished exporting 3mf \n " ) ;
if ( proFn ) {
proFn ( EXPORT_STAGE_FINISH , 0 , 1 , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
lock . filename = nullptr ;
return true ;
}
bool _QDS_3MF_Exporter : : _add_file_to_archive ( mz_zip_archive & archive , const std : : string & path_in_zip , const std : : string & src_file_path )
{
static std : : string const nocomp_exts [ ] = { " .png " , " .jpg " , " .mp4 " , " .jpeg " , " .zip " , " .3mf " } ;
auto end = nocomp_exts + sizeof ( nocomp_exts ) / sizeof ( nocomp_exts [ 0 ] ) ;
bool nocomp = std : : find_if ( nocomp_exts , end , [ & path_in_zip ] ( auto & ext ) { return boost : : algorithm : : ends_with ( path_in_zip , ext ) ; } ) ! = end ;
# if WRITE_ZIP_LANGUAGE_ENCODING
bool result = mz_zip_writer_add_file ( & archive , path_in_zip . c_str ( ) , encode_path ( src_file_path . c_str ( ) ) . c_str ( ) , NULL , 0 , nocomp ? MZ_NO_COMPRESSION : MZ_DEFAULT_LEVEL ) ;
# else
std : : string native_path = encode_path ( path_in_zip . c_str ( ) ) ;
std : : string extra = ZipUnicodePathExtraField : : encode ( path_in_zip , native_path ) ;
bool result = mz_zip_writer_add_file_ex ( & archive , native_path . c_str ( ) , encode_path ( src_file_path . c_str ( ) ) . c_str ( ) , NULL , 0 , nocomp ? MZ_ZIP_FLAG_ASCII_FILENAME : MZ_DEFAULT_COMPRESSION ,
extra . c_str ( ) , extra . length ( ) , extra . c_str ( ) , extra . length ( ) ) ;
# endif
if ( ! result ) {
add_error ( " Unable to add file " + src_file_path + " to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add file %1% to archive %2% \n " ) % src_file_path % path_in_zip ;
} else {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , add file %1% to archive %2% \n " ) % src_file_path % path_in_zip ;
}
return result ;
}
bool _QDS_3MF_Exporter : : _add_content_types_file_to_archive ( mz_zip_archive & archive )
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <Types xmlns= \" http://schemas.openxmlformats.org/package/2006/content-types \" > \n " ;
stream < < " <Default Extension= \" rels \" ContentType= \" application/vnd.openxmlformats-package.relationships+xml \" /> \n " ;
stream < < " <Default Extension= \" model \" ContentType= \" application/vnd.ms-package.3dmanufacturing-3dmodel+xml \" /> \n " ;
stream < < " <Default Extension= \" png \" ContentType= \" image/png \" /> \n " ;
stream < < " <Default Extension= \" gcode \" ContentType= \" text/x.gcode \" /> \n " ;
stream < < " </Types> " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , CONTENT_TYPES_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add content types file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add content types file to archive \n " ) ;
return false ;
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_thumbnail_file_to_archive ( mz_zip_archive & archive , const ThumbnailData & thumbnail_data , const char * local_path , int index , bool generate_small_thumbnail )
{
bool res = false ;
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_COMPRESSION , 1 ) ;
if ( png_data ! = nullptr ) {
std : : string thumbnail_name = ( boost : : format ( " %1%_%2%.png " ) % local_path % ( index + 1 ) ) . str ( ) ;
res = mz_zip_writer_add_mem ( & archive , thumbnail_name . c_str ( ) , ( const void * ) png_data , png_size , MZ_NO_COMPRESSION ) ;
mz_free ( png_data ) ;
}
if ( ! res ) {
add_error ( " Unable to add thumbnail file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add thumbnail file to archive \n " ) ;
}
if ( generate_small_thumbnail & & thumbnail_data . is_valid ( ) ) {
//generate small size of thumbnail
std : : vector < unsigned char > small_pixels ;
small_pixels . resize ( PLATE_THUMBNAIL_SMALL_WIDTH * PLATE_THUMBNAIL_SMALL_HEIGHT * 4 ) ;
/* step width and step height */
int sw = thumbnail_data . width / PLATE_THUMBNAIL_SMALL_WIDTH ;
int sh = thumbnail_data . height / PLATE_THUMBNAIL_SMALL_HEIGHT ;
int clampped_width = sw * PLATE_THUMBNAIL_SMALL_WIDTH ;
int clampped_height = sh * PLATE_THUMBNAIL_SMALL_HEIGHT ;
for ( int i = 0 ; i < clampped_height ; i + = sh ) {
for ( int j = 0 ; j < clampped_width ; j + = sw ) {
int r = 0 , g = 0 , b = 0 , a = 0 ;
for ( int m = 0 ; m < sh ; m + + ) {
for ( int n = 0 ; n < sw ; n + + ) {
r + = ( int ) thumbnail_data . pixels [ 4 * ( ( i + m ) * thumbnail_data . width + j + n ) + 0 ] ;
g + = ( int ) thumbnail_data . pixels [ 4 * ( ( i + m ) * thumbnail_data . width + j + n ) + 1 ] ;
b + = ( int ) thumbnail_data . pixels [ 4 * ( ( i + m ) * thumbnail_data . width + j + n ) + 2 ] ;
a + = ( int ) thumbnail_data . pixels [ 4 * ( ( i + m ) * thumbnail_data . width + j + n ) + 3 ] ;
}
}
r = std : : clamp ( 0 , r / sw / sh , 255 ) ;
g = std : : clamp ( 0 , g / sw / sh , 255 ) ;
b = std : : clamp ( 0 , b / sw / sh , 255 ) ;
a = std : : clamp ( 0 , a / sw / sh , 255 ) ;
small_pixels [ 4 * ( i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh ) + 0 ] = ( unsigned char ) r ;
small_pixels [ 4 * ( i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh ) + 1 ] = ( unsigned char ) g ;
small_pixels [ 4 * ( i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh ) + 2 ] = ( unsigned char ) b ;
small_pixels [ 4 * ( i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh ) + 3 ] = ( unsigned char ) a ;
//memcpy((void*)&small_pixels[4*(i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh)], thumbnail_data.pixels.data() + 4*(i * thumbnail_data.width + j), 4);
}
}
size_t small_png_size = 0 ;
void * small_png_data = tdefl_write_image_to_png_file_in_memory_ex ( ( const void * ) small_pixels . data ( ) , PLATE_THUMBNAIL_SMALL_WIDTH , PLATE_THUMBNAIL_SMALL_HEIGHT , 4 , & small_png_size , MZ_DEFAULT_COMPRESSION , 1 ) ;
if ( png_data ! = nullptr ) {
std : : string thumbnail_name = ( boost : : format ( " %1%_%2%_small.png " ) % local_path % ( index + 1 ) ) . str ( ) ;
res = mz_zip_writer_add_mem ( & archive , thumbnail_name . c_str ( ) , ( const void * ) small_png_data , small_png_size , MZ_NO_COMPRESSION ) ;
mz_free ( small_png_data ) ;
}
if ( ! res ) {
add_error ( " Unable to add small thumbnail file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add small thumbnail file to archive \n " ) ;
}
}
return res ;
}
bool _QDS_3MF_Exporter : : _add_calibration_file_to_archive ( mz_zip_archive & archive , const ThumbnailData & thumbnail_data , int index )
{
bool res = false ;
/*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_COMPRESSION , 1 ) ;
if ( png_data ! = nullptr ) {
std : : string thumbnail_name = ( boost : : format ( PATTERN_FILE_FORMAT ) % ( index + 1 ) ) . str ( ) ;
res = mz_zip_writer_add_mem ( & archive , thumbnail_name . c_str ( ) , ( const void * ) png_data , png_size , MZ_NO_COMPRESSION ) ;
mz_free ( png_data ) ;
}
if ( ! res ) {
add_error ( " Unable to add thumbnail file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add thumbnail file to archive \n " ) ;
} */
return res ;
}
bool _QDS_3MF_Exporter : : _add_bbox_file_to_archive ( mz_zip_archive & archive , const PlateBBoxData & id_bboxes , int index )
{
bool res = false ;
nlohmann : : json j ;
id_bboxes . to_json ( j ) ;
std : : string out = j . dump ( ) ;
std : : string json_file_name = ( boost : : format ( PATTERN_CONFIG_FILE_FORMAT ) % ( index + 1 ) ) . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , json_file_name . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add json file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add json file to archive \n " ) ;
return false ;
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_relationships_file_to_archive (
mz_zip_archive & archive , std : : string const & from , std : : vector < std : : string > const & targets , std : : vector < std : : string > const & types , PackingTemporaryData data , int export_plate_idx ) const
{
std : : stringstream stream ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <Relationships xmlns= \" http://schemas.openxmlformats.org/package/2006/relationships \" > \n " ;
if ( from . empty ( ) ) {
stream < < " <Relationship Target= \" / " < < MODEL_FILE < < " \" Id= \" rel-1 \" Type= \" http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel \" /> \n " ;
if ( export_plate_idx < 0 ) {
//use cover image if have
if ( data . _3mf_thumbnail . empty ( ) ) {
stream < < " <Relationship Target= \" /Metadata/plate_1.png "
< < " \" Id= \" rel-2 \" Type= \" http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail \" /> \n " ;
} else {
stream < < " <Relationship Target= \" / " < < xml_escape ( data . _3mf_thumbnail )
< < " \" Id= \" rel-2 \" Type= \" http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail \" /> \n " ;
}
if ( data . _3mf_printer_thumbnail_middle . empty ( ) ) {
stream < < " <Relationship Target= \" /Metadata/plate_1.png "
< < " \" Id= \" rel-4 \" Type= \" http://schemas.qidilab.com/package/2021/cover-thumbnail-middle \" /> \n " ;
} else {
stream < < " <Relationship Target= \" / " < < xml_escape ( data . _3mf_printer_thumbnail_middle )
< < " \" Id= \" rel-4 \" Type= \" http://schemas.qidilab.com/package/2021/cover-thumbnail-middle \" /> \n " ;
}
if ( data . _3mf_printer_thumbnail_small . empty ( ) ) {
stream < < " <Relationship Target= \" /Metadata/plate_1_small.png "
< < " \" Id= \" rel-5 \" Type= \" http://schemas.qidilab.com/package/2021/cover-thumbnail-small \" /> \n " ;
} else {
stream < < " <Relationship Target= \" / " < < xml_escape ( data . _3mf_printer_thumbnail_small )
< < " \" Id= \" rel-5 \" Type= \" http://schemas.qidilab.com/package/2021/cover-thumbnail-small \" /> \n " ;
}
}
else {
//always use plate thumbnails
std : : string thumbnail_file_str = ( boost : : format ( " Metadata/plate_%1%.png " ) % ( export_plate_idx + 1 ) ) . str ( ) ;
stream < < " <Relationship Target= \" / " < < xml_escape ( thumbnail_file_str )
< < " \" Id= \" rel-2 \" Type= \" http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail \" /> \n " ;
thumbnail_file_str = ( boost : : format ( " Metadata/plate_%1%.png " ) % ( export_plate_idx + 1 ) ) . str ( ) ;
stream < < " <Relationship Target= \" / " < < xml_escape ( thumbnail_file_str )
< < " \" Id= \" rel-4 \" Type= \" http://schemas.qidilab.com/package/2021/cover-thumbnail-middle \" /> \n " ;
thumbnail_file_str = ( boost : : format ( " Metadata/plate_%1%_small.png " ) % ( export_plate_idx + 1 ) ) . str ( ) ;
stream < < " <Relationship Target= \" / " < < xml_escape ( thumbnail_file_str )
< < " \" Id= \" rel-5 \" Type= \" http://schemas.qidilab.com/package/2021/cover-thumbnail-small \" /> \n " ;
}
}
else if ( targets . empty ( ) ) {
return false ;
}
else {
int i = 0 ;
for ( auto & path : targets ) {
for ( auto & type : types )
stream < < " <Relationship Target= \" / " < < xml_escape ( path ) < < " \" Id= \" rel- " < < boost : : to_string ( + + i ) < < " \" Type= \" " < < type < < " \" /> \n " ;
}
}
stream < < " </Relationships> " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , from . empty ( ) ? RELATIONSHIPS_FILE . c_str ( ) : from . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add relationships file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add relationships file to archive \n " ) ;
return false ;
}
return true ;
}
static void reset_stream ( std : : stringstream & stream )
{
stream . str ( " " ) ;
stream . clear ( ) ;
// 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!
stream < < std : : setprecision ( std : : numeric_limits < float > : : max_digits10 ) ;
}
/*
* QDS : Production Extension ( SplitModel )
* save sub model if objects_data is not empty
* not collect build items in sub model
*/
bool _QDS_3MF_Exporter : : _add_model_file_to_archive ( const std : : string & filename , mz_zip_archive & archive , const Model & model , ObjectToObjectDataMap & objects_data , Export3mfProgressFn proFn , QDTProject * project ) const
{
bool sub_model = ! objects_data . empty ( ) ;
bool write_object = sub_model | | ! m_split_model ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , filename %1%, m_split_model %2%, sub_model %3% " ) % filename % m_split_model % sub_model ;
# if WRITE_ZIP_LANGUAGE_ENCODING
auto & zip_filename = filename ;
# else
std : : string zip_filename = encode_path ( filename . c_str ( ) ) ;
std : : string extra = sub_model ? ZipUnicodePathExtraField : : encode ( filename , zip_filename ) : " " ;
# endif
mz_zip_writer_staged_context context ;
if ( ! mz_zip_writer_add_staged_open ( & archive , & context , sub_model ? zip_filename . c_str ( ) : MODEL_FILE . c_str ( ) ,
m_zip64 ?
// Maximum expected and allowed 3MF file size is 16GiB.
// This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
( uint64_t ( 1 ) < < 30 ) * 16 :
// Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
// GH issue #6193.
( uint64_t ( 1 ) < < 32 ) - 1 ,
# if WRITE_ZIP_LANGUAGE_ENCODING
nullptr , nullptr , 0 , MZ_DEFAULT_LEVEL , nullptr , 0 , nullptr , 0 ) ) {
# else
nullptr , nullptr , 0 , MZ_DEFAULT_COMPRESSION , extra . c_str ( ) , extra . length ( ) , extra . c_str ( ) , extra . length ( ) ) ) {
# endif
add_error ( " Unable to add model file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add model file to archive \n " ) ;
return false ;
}
{
std : : stringstream stream ;
reset_stream ( stream ) ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " < " < < MODEL_TAG < < " unit= \" millimeter \" xml:lang= \" en-US \" xmlns= \" http://schemas.microsoft.com/3dmanufacturing/core/2015/02 \" xmlns:QIDIStudio= \" http://schemas.qidilab.com/package/2021 \" " ;
if ( m_production_ext )
stream < < " xmlns:p= \" http://schemas.microsoft.com/3dmanufacturing/production/2015/06 \" requiredextensions= \" p \" " ;
stream < < " > \n " ;
std : : string origin ;
std : : string name ;
std : : string user_name ;
std : : string user_id ;
std : : string design_cover ;
std : : string license ;
std : : string description ;
std : : string copyright ;
std : : string rating ;
std : : string model_id ;
std : : string region_code ;
if ( model . design_info ) {
user_name = model . design_info - > Designer ;
user_id = model . design_info - > DesignerUserId ;
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, save_3mf found designer = " < < user_name ;
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, save_3mf found designer_user_id = " < < user_id ;
}
if ( project ) {
model_id = project - > project_model_id ;
region_code = project - > project_country_code ;
}
if ( model . model_info ) {
design_cover = model . model_info - > cover_file ;
license = model . model_info - > license ;
description = model . model_info - > description ;
copyright = model . model_info - > copyright ;
name = model . model_info - > model_name ;
origin = model . model_info - > origin ;
BOOST_LOG_TRIVIAL ( trace ) < < " design_info, save_3mf found designer_cover = " < < design_cover ;
}
// remember to use metadata_item_map to store metadata info
std : : map < std : : string , std : : string > metadata_item_map ;
if ( ! sub_model ) {
// update metadat_items
if ( model . model_info & & model . model_info . get ( ) ) {
metadata_item_map = model . model_info . get ( ) - > metadata_items ;
}
metadata_item_map [ QDT_MODEL_NAME_TAG ] = xml_escape ( name ) ;
metadata_item_map [ QDT_ORIGIN_TAG ] = xml_escape ( origin ) ;
metadata_item_map [ QDT_DESIGNER_TAG ] = xml_escape ( user_name ) ;
metadata_item_map [ QDT_DESIGNER_USER_ID_TAG ] = user_id ;
metadata_item_map [ QDT_DESIGNER_COVER_FILE_TAG ] = xml_escape ( design_cover ) ;
metadata_item_map [ QDT_DESCRIPTION_TAG ] = xml_escape ( description ) ;
metadata_item_map [ QDT_COPYRIGHT_NORMATIVE_TAG ] = xml_escape ( copyright ) ;
metadata_item_map [ QDT_LICENSE_TAG ] = xml_escape ( license ) ;
/* save model info */
if ( ! model_id . empty ( ) ) {
metadata_item_map [ QDT_MODEL_ID_TAG ] = model_id ;
metadata_item_map [ QDT_REGION_TAG ] = region_code ;
}
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 ) ;
metadata_item_map [ QDT_CREATION_DATE_TAG ] = date ;
metadata_item_map [ QDT_MODIFICATION_TAG ] = date ;
metadata_item_map [ QDT_APPLICATION_TAG ] = ( boost : : format ( " %1%-%2% " ) % SLIC3R_APP_KEY % SLIC3R_VERSION ) . str ( ) ;
}
metadata_item_map [ QDS_3MF_VERSION ] = std : : to_string ( VERSION_QDS_3MF ) ;
if ( ! model . mk_name . empty ( ) ) {
metadata_item_map [ QDT_MAKERLAB_TAG ] = xml_escape ( model . mk_name ) ;
BOOST_LOG_TRIVIAL ( info ) < < " saved mk_name " < < model . mk_name ;
}
if ( ! model . mk_version . empty ( ) ) {
metadata_item_map [ QDT_MAKERLAB_VERSION_TAG ] = xml_escape ( model . mk_version ) ;
BOOST_LOG_TRIVIAL ( info ) < < " saved mk_version " < < model . mk_version ;
}
if ( ! model . md_name . empty ( ) ) {
for ( unsigned int i = 0 ; i < model . md_name . size ( ) ; i + + )
{
BOOST_LOG_TRIVIAL ( info ) < < boost : : format ( " saved metadata_name %1%, metadata_value %2% " ) % model . md_name [ i ] % model . md_value [ i ] ;
metadata_item_map [ model . md_name [ i ] ] = xml_escape ( model . md_value [ i ] ) ;
}
}
// store metadata info
for ( auto item : metadata_item_map ) {
BOOST_LOG_TRIVIAL ( info ) < < " qds_3mf: save key= " < < item . first < < " , value = " < < item . second ;
stream < < " < " < < METADATA_TAG < < " name= \" " < < item . first < < " \" > "
< < xml_escape ( item . second ) < < " </ " < < METADATA_TAG < < " > \n " ;
}
stream < < " < " < < RESOURCES_TAG < < " > \n " ;
std : : string buf = stream . str ( ) ;
if ( ! buf . empty ( ) & & ! mz_zip_writer_add_staged_data ( & context , buf . data ( ) , buf . size ( ) ) ) {
add_error ( " Unable to add model file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add model file to archive \n " ) ;
return false ;
}
}
// Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
BuildItemsList build_items ;
// 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.
unsigned int object_id = 1 ;
unsigned int object_index = 0 ;
bool cb_cancel = false ;
std : : vector < std : : string > object_paths ;
// if (!m_skip_model) {
for ( ModelObject * obj : model . objects ) {
if ( sub_model & & obj ! = objects_data . begin ( ) - > second . object ) continue ;
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_MODELS , object_index + + , model . objects . size ( ) , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
if ( obj = = nullptr )
continue ;
// Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
ObjectToObjectDataMap : : iterator object_it = objects_data . begin ( ) ;
if ( ! sub_model ) {
// For backup, use backup id as object id
int backup_id = const_cast < Model & > ( model ) . get_object_backup_id ( * obj ) ;
if ( m_from_backup_save ) object_id = backup_id ;
object_it = objects_data . insert ( { obj , { obj , backup_id } } ) . first ;
auto & object_data = object_it - > second ;
if ( m_split_model ) {
auto filename = boost : : format ( " 3D/Objects/object_%d.model " ) % backup_id ;
object_data . sub_path = " / " + filename . str ( ) ;
object_paths . push_back ( filename . str ( ) ) ;
}
auto & volumes_objectID = object_data . volumes_objectID ;
unsigned int volume_id = object_id , volume_count = 0 ;
for ( ModelVolume * volume : obj - > volumes ) {
if ( volume = = nullptr )
continue ;
volume_count + + ;
if ( m_share_mesh ) {
auto iter = m_shared_meshes . find ( volume - > mesh_ptr ( ) ) ;
if ( iter ! = m_shared_meshes . end ( ) )
{
const ModelVolume * shared_volume = iter - > second . second ;
if ( ( shared_volume - > supported_facets . equals ( volume - > supported_facets ) )
& & ( shared_volume - > seam_facets . equals ( volume - > seam_facets ) )
& & ( shared_volume - > mmu_segmentation_facets . equals ( volume - > mmu_segmentation_facets ) ) )
{
auto data = iter - > second . first ;
const_cast < _QDS_3MF_Exporter * > ( this ) - > m_volume_paths . insert ( { volume , { data - > sub_path , data - > volumes_objectID . find ( iter - > second . second ) - > second } } ) ;
volumes_objectID . insert ( { volume , 0 } ) ;
object_data . share_mesh = true ;
continue ;
}
}
const_cast < _QDS_3MF_Exporter * > ( this ) - > m_shared_meshes . insert ( { volume - > mesh_ptr ( ) , { & object_data , volume } } ) ;
}
if ( m_from_backup_save )
volume_id = ( volume_count < < 16 | backup_id ) ;
volumes_objectID . insert ( { volume , volume_id } ) ;
volume_id + + ;
}
if ( ! m_from_backup_save ) object_id = volume_id ;
object_data . object_id = object_id ;
}
if ( m_skip_model ) continue ;
if ( write_object ) {
// Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
// object_it->second.volumes_objectID 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.
if ( ! _add_object_to_model_stream ( context , object_it - > second ) ) {
add_error ( " Unable to add object to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add object to archive \n " ) ;
return false ;
}
}
if ( sub_model ) break ;
unsigned int count = 0 ;
for ( const ModelInstance * instance : obj - > instances ) {
Transform3d t = instance - > get_matrix ( ) ;
// instance_id is just a 1 indexed index in build_items.
//assert(m_skip_static || curr_id == build_items.size() + 1);
build_items . emplace_back ( " " , object_it - > second . object_id , t , instance - > printable ) ;
count + + ;
}
if ( ! m_from_backup_save ) object_id + + ;
}
// }
{
std : : stringstream stream ;
reset_stream ( stream ) ;
if ( ! m_skip_model & & ! sub_model ) {
for ( auto object : model . objects ) {
auto & data = objects_data [ object ] ;
_add_object_components_to_stream ( stream , data ) ;
}
}
stream < < " </ " < < RESOURCES_TAG < < " > \n " ;
// Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
if ( ! _add_build_to_model_stream ( stream , build_items ) ) {
add_error ( " Unable to add build to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add build to archive \n " ) ;
return false ;
}
stream < < " </ " < < MODEL_TAG < < " > \n " ;
std : : string buf = stream . str ( ) ;
if ( ( ! buf . empty ( ) & & ! mz_zip_writer_add_staged_data ( & context , buf . data ( ) , buf . size ( ) ) ) | |
! mz_zip_writer_add_staged_finish ( & context ) ) {
add_error ( " Unable to add model file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add model file to archive \n " ) ;
return false ;
}
}
if ( m_skip_model | | write_object ) return true ;
// write model rels
_add_relationships_file_to_archive ( archive , MODEL_RELS_FILE , object_paths , { " http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel " } ) ;
if ( ! m_from_backup_save ) {
boost : : mutex mutex ;
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , objects_data . size ( ) , 1 ) , [ this , & mutex , & model , objects = model . objects , & objects_data , & object_paths , main = & archive , project ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t i = range . begin ( ) ; i < range . end ( ) ; + + i ) {
auto iter = objects_data . find ( objects [ i ] ) ;
ObjectToObjectDataMap objects_data2 ;
objects_data2 . insert ( * iter ) ;
auto & object = * iter - > second . object ;
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
mz_zip_writer_init_heap ( & archive , 0 , 1024 * 1024 ) ;
CNumericLocalesSetter locales_setter ;
_add_model_file_to_archive ( object_paths [ i ] , archive , model , objects_data2 , nullptr , project ) ;
iter - > second = objects_data2 . begin ( ) - > second ;
void * ppBuf ; size_t pSize ;
mz_zip_writer_finalize_heap_archive ( & archive , & ppBuf , & pSize ) ;
mz_zip_writer_end ( & archive ) ;
mz_zip_zero_struct ( & archive ) ;
mz_zip_reader_init_mem ( & archive , ppBuf , pSize , 0 ) ;
{
boost : : unique_lock l ( mutex ) ;
mz_zip_writer_add_from_zip_reader ( main , & archive , 0 ) ;
}
mz_zip_reader_end ( & archive ) ;
}
} ) ;
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_object_to_model_stream ( mz_zip_writer_staged_context & context , ObjectData const & object_data ) const
{
// backup: make _add_mesh_to_object_stream() reusable
auto flush = [ this , & context ] ( std : : string & buf , bool force = false ) {
if ( ( force & & ! buf . empty ( ) ) | | buf . size ( ) > = 65536 * 16 ) {
if ( ! mz_zip_writer_add_staged_data ( & context , buf . data ( ) , buf . size ( ) ) ) {
add_error ( " Error during writing or compression " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Error during writing or compression \n " ) ;
return false ;
}
buf . clear ( ) ;
}
return true ;
} ;
if ( ! _add_mesh_to_object_stream ( flush , object_data ) ) {
add_error ( " Unable to add mesh to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add mesh to archive \n " ) ;
return false ;
}
// Move all components to main model
//_add_object_components_to_stream(stream, object_data);
return true ;
}
void _QDS_3MF_Exporter : : _add_object_components_to_stream ( std : : stringstream & stream , ObjectData const & object_data ) const
{
auto & object = * object_data . object ;
stream < < " < " < < OBJECT_TAG < < " id= \" " < < object_data . object_id ;
if ( m_production_ext )
stream < < " \" " < < PUUID_ATTR < < " = \" " < < hex_wrap < boost : : uint32_t > { ( boost : : uint32_t ) object_data . backup_id }
< < ( object_data . share_mesh ? OBJECT_UUID_SUFFIX2 : OBJECT_UUID_SUFFIX ) ;
stream < < " \" type= \" model \" > \n " ;
stream < < " < " < < COMPONENTS_TAG < < " > \n " ;
for ( unsigned int index = 0 ; index < object . volumes . size ( ) ; index + + ) {
ModelVolume * volume = object . volumes [ index ] ;
unsigned int volume_id = object_data . volumes_objectID . find ( volume ) - > second ;
auto * ppath = & object_data . sub_path ;
auto iter = m_volume_paths . find ( volume ) ;
if ( iter ! = m_volume_paths . end ( ) ) {
ppath = & iter - > second . first ;
volume_id = iter - > second . second ;
}
//add the transform of the volume
if ( ppath - > empty ( ) )
stream < < " < " < < COMPONENT_TAG < < " objectid= \" " < < volume_id ;
else
stream < < " < " < < COMPONENT_TAG < < " p:path= \" " < < xml_escape ( * ppath ) < < " \" objectid= \" " < < volume_id ; // << "\"/>\n";
if ( m_production_ext )
stream < < " \" " < < PUUID_ATTR < < " = \" " < < hex_wrap < boost : : uint32_t > { ( boost : : uint32_t ) ( index + ( object_data . backup_id < < 16 ) ) } < < COMPONENT_UUID_SUFFIX ;
const Transform3d & transf = volume - > get_matrix ( ) ;
stream < < " \" " < < TRANSFORM_ATTR < < " = \" " ;
for ( unsigned c = 0 ; c < 4 ; + + c ) {
for ( unsigned r = 0 ; r < 3 ; + + r ) {
stream < < transf ( r , c ) ;
if ( r ! = 2 | | c ! = 3 )
stream < < " " ;
}
}
stream < < " \" /> \n " ;
}
stream < < " </ " < < COMPONENTS_TAG < < " > \n " ;
stream < < " </ " < < OBJECT_TAG < < " > \n " ;
}
# if EXPORT_3MF_USE_SPIRIT_KARMA_FP
template < typename Num >
struct coordinate_policy_fixed : boost : : spirit : : karma : : real_policies < Num >
{
static int floatfield ( Num n ) { return fmtflags : : fixed ; }
// Number of decimal digits to maintain float accuracy when storing into a text file and parsing back.
static unsigned precision ( Num /* n */ ) { return std : : numeric_limits < Num > : : max_digits10 + 1 ; }
// No trailing zeros, thus for fmtflags::fixed usually much less than max_digits10 decimal numbers will be produced.
static bool trailing_zeros ( Num /* n */ ) { return false ; }
} ;
template < typename Num >
struct coordinate_policy_scientific : coordinate_policy_fixed < Num >
{
static int floatfield ( Num n ) { return fmtflags : : scientific ; }
} ;
// Define a new generator type based on the new coordinate policy.
using coordinate_type_fixed = boost : : spirit : : karma : : real_generator < float , coordinate_policy_fixed < float > > ;
using coordinate_type_scientific = boost : : spirit : : karma : : real_generator < float , coordinate_policy_scientific < float > > ;
# endif // EXPORT_3MF_USE_SPIRIT_KARMA_FP
//QDS: change volume to seperate objects
bool _QDS_3MF_Exporter : : _add_mesh_to_object_stream ( std : : function < bool ( std : : string & , bool ) > const & flush , ObjectData const & object_data ) const
{
std : : string output_buffer ;
#if 0
auto flush = [ this , & output_buffer , & context ] ( bool force = false ) {
if ( ( force & & ! output_buffer . empty ( ) ) | | output_buffer . size ( ) > = 65536 * 16 ) {
if ( ! mz_zip_writer_add_staged_data ( & context , output_buffer . data ( ) , output_buffer . size ( ) ) ) {
add_error ( " Error during writing or compression " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Error during writing or compression \n " ) ;
return false ;
}
output_buffer . clear ( ) ;
}
return true ;
} ;
# endif
/*output_buffer += " <";
output_buffer + = MESH_TAG ;
output_buffer + = " > \n < " ;
output_buffer + = VERTICES_TAG ;
output_buffer + = " > \n " ; */
auto format_coordinate = [ ] ( float f , char * buf ) - > char * {
assert ( is_decimal_separator_point ( ) ) ;
# if EXPORT_3MF_USE_SPIRIT_KARMA_FP
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
// https://github.com/boostorg/spirit/pull/586
// where the exported string is one digit shorter than it should be to guarantee lossless round trip.
// The code is left here for the ocasion boost guys improve.
coordinate_type_fixed const coordinate_fixed = coordinate_type_fixed ( ) ;
coordinate_type_scientific const coordinate_scientific = coordinate_type_scientific ( ) ;
// Format "f" in a fixed format.
char * ptr = buf ;
boost : : spirit : : karma : : generate ( ptr , coordinate_fixed , f ) ;
// Format "f" in a scientific format.
char * ptr2 = ptr ;
boost : : spirit : : karma : : generate ( ptr2 , coordinate_scientific , f ) ;
// Return end of the shorter string.
auto len2 = ptr2 - ptr ;
if ( ptr - buf > len2 ) {
// Move the shorter scientific form to the front.
memcpy ( buf , ptr , len2 ) ;
ptr = buf + len2 ;
}
// Return pointer to the end.
return ptr ;
# else
// Round-trippable float, shortest possible.
return buf + sprintf ( buf , " %.9g " , f ) ;
# endif
} ;
auto const & object = * object_data . object ;
char buf [ 256 ] ;
unsigned int vertices_count = 0 ;
//unsigned int triangles_count = 0;
for ( unsigned int index = 0 ; index < object . volumes . size ( ) ; index + + ) {
ModelVolume * volume = object . volumes [ index ] ;
if ( volume = = nullptr )
continue ;
int volume_id = object_data . volumes_objectID . find ( volume ) - > second ;
if ( m_share_mesh & & volume_id = = 0 )
continue ;
//if (!volume->mesh().stats().repaired())
// throw Slic3r::FileIOError("store_3mf() requires repair()");
const indexed_triangle_set & its = volume - > mesh ( ) . its ;
if ( its . vertices . empty ( ) ) {
add_error ( " Found invalid mesh " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Found invalid mesh \n " ) ;
return false ;
}
std : : string type = ( volume - > type ( ) = = ModelVolumeType : : MODEL_PART ) ? " model " : " other " ;
output_buffer + = " < " ;
output_buffer + = OBJECT_TAG ;
output_buffer + = " id= \" " ;
output_buffer + = std : : to_string ( volume_id ) ;
if ( m_production_ext ) {
std : : stringstream stream ;
reset_stream ( stream ) ;
stream < < " \" " < < PUUID_ATTR < < " = \" " < < hex_wrap < boost : : uint32_t > { ( boost : : uint32_t ) ( index + ( object_data . backup_id < < 16 ) ) } < < SUB_OBJECT_UUID_SUFFIX ;
//output_buffer += "\" ";
//output_buffer += PUUID_ATTR;
//output_buffer += "=\"";
//output_buffer += std::to_string(hex_wrap<boost::uint32_t>{(boost::uint32_t)backup_id});
//output_buffer += OBJECT_UUID_SUFFIX;
output_buffer + = stream . str ( ) ;
}
output_buffer + = " \" type= \" " ;
output_buffer + = type ;
output_buffer + = " \" > \n " ;
output_buffer + = " < " ;
output_buffer + = MESH_TAG ;
output_buffer + = " > \n < " ;
output_buffer + = VERTICES_TAG ;
output_buffer + = " > \n " ;
vertices_count + = ( int ) its . vertices . size ( ) ;
for ( size_t i = 0 ; i < its . vertices . size ( ) ; + + i ) {
//don't save the volume's matrix into vertex data
//add the shared mesh logic
//Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
Vec3f v = its . vertices [ i ] ;
char * ptr = buf ;
boost : : spirit : : karma : : generate ( ptr , boost : : spirit : : lit ( " < " ) < < VERTEX_TAG < < " x= \" " ) ;
ptr = format_coordinate ( v . x ( ) , ptr ) ;
boost : : spirit : : karma : : generate ( ptr , " \" y= \" " ) ;
ptr = format_coordinate ( v . y ( ) , ptr ) ;
boost : : spirit : : karma : : generate ( ptr , " \" z= \" " ) ;
ptr = format_coordinate ( v . z ( ) , ptr ) ;
boost : : spirit : : karma : : generate ( ptr , " \" /> \n " ) ;
* ptr = ' \0 ' ;
output_buffer + = buf ;
if ( ! flush ( output_buffer , false ) )
return false ;
}
//}
output_buffer + = " </ " ;
output_buffer + = VERTICES_TAG ;
output_buffer + = " > \n < " ;
output_buffer + = TRIANGLES_TAG ;
output_buffer + = " > \n " ;
//for (ModelVolume* volume : object.volumes) {
// if (volume == nullptr)
// continue;
//QDS: as we stored matrix seperately, not multiplied into vertex
//we don't need to consider this left hand case specially
//bool is_left_handed = volume->is_left_handed();
bool is_left_handed = false ;
//VolumeToOffsetsMap::iterator volume_it = volumes_objectID.find(volume);
//assert(volume_it != volumes_objectID.end());
//const indexed_triangle_set &its = volume->mesh().its;
// updates triangle offsets
//unsigned int first_triangle_id = triangles_count;
//triangles_count += (int)its.indices.size();
//unsigned int last_triangle_id = triangles_count - 1;
for ( int i = 0 ; i < int ( its . indices . size ( ) ) ; + + i ) {
{
const Vec3i & idx = its . indices [ i ] ;
char * ptr = buf ;
boost : : spirit : : karma : : generate ( ptr , boost : : spirit : : lit ( " < " ) < < TRIANGLE_TAG < <
" v1= \" " < < boost : : spirit : : int_ < <
" \" v2= \" " < < boost : : spirit : : int_ < <
" \" v3= \" " < < boost : : spirit : : int_ < < " \" " ,
idx [ is_left_handed ? 2 : 0 ] ,
idx [ 1 ] ,
idx [ is_left_handed ? 0 : 2 ] ) ;
* ptr = ' \0 ' ;
output_buffer + = buf ;
}
std : : string custom_supports_data_string = volume - > supported_facets . get_triangle_as_string ( i ) ;
if ( ! custom_supports_data_string . empty ( ) ) {
output_buffer + = " " ;
output_buffer + = CUSTOM_SUPPORTS_ATTR ;
output_buffer + = " = \" " ;
output_buffer + = custom_supports_data_string ;
output_buffer + = " \" " ;
}
std : : string custom_seam_data_string = volume - > seam_facets . get_triangle_as_string ( i ) ;
if ( ! custom_seam_data_string . empty ( ) ) {
output_buffer + = " " ;
output_buffer + = CUSTOM_SEAM_ATTR ;
output_buffer + = " = \" " ;
output_buffer + = custom_seam_data_string ;
output_buffer + = " \" " ;
}
std : : string mmu_painting_data_string = volume - > mmu_segmentation_facets . get_triangle_as_string ( i ) ;
if ( ! mmu_painting_data_string . empty ( ) ) {
output_buffer + = " " ;
output_buffer + = MMU_SEGMENTATION_ATTR ;
output_buffer + = " = \" " ;
output_buffer + = mmu_painting_data_string ;
output_buffer + = " \" " ;
}
// QDS
if ( i < its . properties . size ( ) ) {
std : : string prop_str = its . properties [ i ] . to_string ( ) ;
if ( ! prop_str . empty ( ) ) {
output_buffer + = " " ;
output_buffer + = FACE_PROPERTY_ATTR ;
output_buffer + = " = \" " ;
output_buffer + = prop_str ;
output_buffer + = " \" " ;
}
}
output_buffer + = " /> \n " ;
if ( ! flush ( output_buffer , false ) )
return false ;
}
output_buffer + = " </ " ;
output_buffer + = TRIANGLES_TAG ;
output_buffer + = " > \n </ " ;
output_buffer + = MESH_TAG ;
output_buffer + = " > \n " ;
output_buffer + = " </ " ;
output_buffer + = OBJECT_TAG ;
output_buffer + = " > \n " ;
}
// Force flush.
return flush ( output_buffer , true ) ;
}
bool _QDS_3MF_Exporter : : _add_build_to_model_stream ( std : : stringstream & stream , const BuildItemsList & build_items ) const
{
// This happens for empty projects
if ( build_items . size ( ) = = 0 ) {
stream < < " < " < < BUILD_TAG < < " /> \n " ;
return true ;
}
stream < < " < " < < BUILD_TAG ;
if ( m_production_ext )
stream < < " " < < PUUID_ATTR < < " = \" " < < BUILD_UUID < < " \" " ;
stream < < " > \n " ;
for ( const BuildItem & item : build_items ) {
stream < < " < " < < ITEM_TAG < < " " < < OBJECTID_ATTR < < " = \" " < < item . id ;
if ( m_production_ext )
stream < < " \" " < < PUUID_ATTR < < " = \" " < < hex_wrap < boost : : uint32_t > { item . id } < < BUILD_UUID_SUFFIX ;
if ( ! item . path . empty ( ) )
stream < < " \" " < < PPATH_ATTR < < " = \" " < < xml_escape ( item . path ) ;
stream < < " \" " < < TRANSFORM_ATTR < < " = \" " ;
for ( unsigned c = 0 ; c < 4 ; + + c ) {
for ( unsigned r = 0 ; r < 3 ; + + r ) {
stream < < item . transform ( r , c ) ;
if ( r ! = 2 | | c ! = 3 )
stream < < " " ;
}
}
stream < < " \" " < < PRINTABLE_ATTR < < " = \" " < < item . printable < < " \" /> \n " ;
}
stream < < " </ " < < BUILD_TAG < < " > \n " ;
return true ;
}
bool _QDS_3MF_Exporter : : _add_layer_height_profile_file_to_archive ( mz_zip_archive & archive , Model & model )
{
assert ( is_decimal_separator_point ( ) ) ;
std : : string out = " " ;
char buffer [ 1024 ] ;
unsigned int count = 0 ;
for ( const ModelObject * object : model . objects ) {
+ + count ;
const std : : vector < double > & layer_height_profile = object - > layer_height_profile . get ( ) ;
if ( layer_height_profile . size ( ) > = 4 & & layer_height_profile . size ( ) % 2 = = 0 ) {
snprintf ( buffer , 1024 , " 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 ) {
snprintf ( buffer , 1024 , ( i = = 0 ) ? " %f " : " ;%f " , layer_height_profile [ i ] ) ;
out + = buffer ;
}
out + = " \n " ;
}
}
if ( ! out . empty ( ) ) {
if ( ! mz_zip_writer_add_mem ( & archive , QDS_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 " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " Unable to add layer heights profile file to archive \n " ) ;
return false ;
}
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_layer_config_ranges_file_to_archive ( mz_zip_archive & archive , Model & model )
{
std : : string out = " " ;
pt : : ptree tree ;
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 ( ) )
{
pt : : ptree & obj_tree = tree . add ( " objects.object " , " " ) ;
obj_tree . put ( " <xmlattr>.id " , object_cnt ) ;
// Store the layer config ranges.
for ( const auto & range : ranges ) {
pt : : ptree & range_tree = obj_tree . add ( " range " , " " ) ;
// store minX and maxZ
range_tree . put ( " <xmlattr>.min_z " , range . first . first ) ;
range_tree . put ( " <xmlattr>.max_z " , range . first . second ) ;
// store range configuration
const ModelConfig & config = range . second ;
for ( const std : : string & opt_key : config . keys ( ) ) {
pt : : ptree & opt_tree = range_tree . add ( " option " , config . opt_serialize ( opt_key ) ) ;
opt_tree . put ( " <xmlattr>.opt_key " , opt_key ) ;
}
}
}
}
if ( ! tree . empty ( ) ) {
std : : ostringstream oss ;
pt : : write_xml ( oss , tree ) ;
out = oss . str ( ) ;
// Post processing("beautification") of the output string for a better preview
boost : : replace_all ( out , " ><object " , " > \n <object " ) ;
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 < " ) ;
}
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 " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " Unable to add layer heights profile file to archive \n " ) ;
return false ;
}
}
return true ;
}
/*
bool _QDS_3MF_Exporter : : _add_sla_support_points_file_to_archive ( mz_zip_archive & archive , Model & model )
{
assert ( is_decimal_separator_point ( ) ) ;
std : : string out = " " ;
char buffer [ 1024 ] ;
unsigned int count = 0 ;
for ( const ModelObject * object : model . objects ) {
+ + count ;
const std : : vector < sla : : SupportPoint > & sla_support_points = object - > sla_support_points ;
if ( ! sla_support_points . empty ( ) ) {
sprintf ( buffer , " object_id=%d| " , count ) ;
out + = buffer ;
// Store the layer height profile as a single space separated list.
for ( size_t i = 0 ; i < sla_support_points . size ( ) ; + + i ) {
sprintf ( buffer , ( i = = 0 ? " %f %f %f %f %f " : " %f %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 ) ;
out + = buffer ;
}
out + = " \n " ;
}
}
if ( ! out . empty ( ) ) {
// Adds version header at the beginning:
//out = std::string("support_points_format_version=") + std::to_string(support_points_format_version) + std::string("\n") + out;
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 " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " Unable to add sla support points file to archive \n " ) ;
return false ;
}
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_sla_drain_holes_file_to_archive ( mz_zip_archive & archive , Model & model )
{
assert ( is_decimal_separator_point ( ) ) ;
const char * const fmt = " object_id=%d| " ;
std : : string out ;
unsigned int count = 0 ;
for ( const ModelObject * object : model . objects ) {
+ + count ;
sla : : DrainHoles drain_holes = object - > sla_drain_holes ;
// The holes were placed 1mm above the mesh in the first implementation.
// This was a bad idea and the reference point was changed in 2.3 so
// to be on the mesh exactly. The elevated position is still saved
// in 3MFs for compatibility reasons.
for ( sla : : DrainHole & hole : drain_holes ) {
hole . pos - = hole . normal . normalized ( ) ;
hole . height + = 1.f ;
}
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 " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " Unable to add sla support points file to archive \n " ) ;
return false ;
}
}
return true ;
} */
bool _QDS_3MF_Exporter : : _add_print_config_file_to_archive ( mz_zip_archive & archive , const DynamicPrintConfig & config )
{
assert ( is_decimal_separator_point ( ) ) ;
char buffer [ 1024 ] ;
snprintf ( buffer , 1024 , " ; %s \n \n " , header_slic3r_generated ( ) . c_str ( ) ) ;
std : : string out = buffer ;
for ( const std : : string & key : config . keys ( ) )
if ( key ! = " compatible_printers " )
out + = " ; " + key + " = " + config . opt_serialize ( key ) + " \n " ;
if ( ! out . empty ( ) ) {
if ( ! mz_zip_writer_add_mem ( & archive , QDS_PRINT_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add print config file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " Unable to add print config file to archive \n " ) ;
return false ;
}
}
return true ;
}
//QDS: add project config file logic for new json format
bool _QDS_3MF_Exporter : : _add_project_config_file_to_archive ( mz_zip_archive & archive , const DynamicPrintConfig & config , Model & model )
{
const std : : string & temp_path = model . get_backup_path ( ) ;
std : : string temp_file = temp_path + std : : string ( " / " ) + " _temp_1.config " ;
config . save_to_json ( temp_file , std : : string ( " project_settings " ) , std : : string ( " project " ) , std : : string ( SLIC3R_VERSION ) ) ;
return _add_file_to_archive ( archive , QDS_PROJECT_CONFIG_FILE , temp_file ) ;
}
//QDS: add project embedded preset files
bool _QDS_3MF_Exporter : : _add_project_embedded_presets_to_archive ( mz_zip_archive & archive , Model & model , std : : vector < Preset * > project_presets )
{
char buffer [ 1024 ] ;
snprintf ( buffer , 1024 , " ; %s \n \n " , header_slic3r_generated ( ) . c_str ( ) ) ;
std : : string out = buffer ;
int print_count = 0 , filament_count = 0 , printer_count = 0 ;
const std : : string & temp_path = model . get_backup_path ( ) ;
for ( int i = 0 ; i < project_presets . size ( ) ; i + + )
{
Preset * preset = project_presets [ i ] ;
if ( preset ) {
preset - > file = temp_path + std : : string ( " / " ) + " _temp_1.config " ;
DynamicPrintConfig & config = preset - > config ;
//config.save(preset->file);
config . save_to_json ( preset - > file , preset - > name , std : : string ( " project " ) , preset - > version . to_string ( ) ) ;
std : : string dest_file ;
if ( preset - > type = = Preset : : TYPE_PRINT ) {
dest_file = ( boost : : format ( EMBEDDED_PRINT_FILE_FORMAT ) % ( print_count + 1 ) ) . str ( ) ;
print_count + + ;
}
else if ( preset - > type = = Preset : : TYPE_FILAMENT ) {
dest_file = ( boost : : format ( EMBEDDED_FILAMENT_FILE_FORMAT ) % ( filament_count + 1 ) ) . str ( ) ;
filament_count + + ;
}
else if ( preset - > type = = Preset : : TYPE_PRINTER ) {
dest_file = ( boost : : format ( EMBEDDED_PRINTER_FILE_FORMAT ) % ( printer_count + 1 ) ) . str ( ) ;
printer_count + + ;
}
else
continue ;
_add_file_to_archive ( archive , dest_file , preset - > file ) ;
}
}
return true ;
}
void _add_text_info_to_archive ( std : : stringstream & stream , const TextInfo & text_info ) {
stream < < " < " < < TEXT_INFO_TAG < < " " ;
stream < < TEXT_ATTR < < " = \" " < < xml_escape ( text_info . m_text ) < < " \" " ;
stream < < FONT_NAME_ATTR < < " = \" " < < text_info . m_font_name < < " \" " ;
stream < < FONT_INDEX_ATTR < < " = \" " < < text_info . m_curr_font_idx < < " \" " ;
stream < < FONT_SIZE_ATTR < < " = \" " < < text_info . m_font_size < < " \" " ;
stream < < THICKNESS_ATTR < < " = \" " < < text_info . m_thickness < < " \" " ;
stream < < EMBEDED_DEPTH_ATTR < < " = \" " < < text_info . m_embeded_depth < < " \" " ;
stream < < ROTATE_ANGLE_ATTR < < " = \" " < < text_info . m_rotate_angle < < " \" " ;
stream < < TEXT_GAP_ATTR < < " = \" " < < text_info . m_text_gap < < " \" " ;
stream < < BOLD_ATTR < < " = \" " < < ( text_info . m_bold ? 1 : 0 ) < < " \" " ;
stream < < ITALIC_ATTR < < " = \" " < < ( text_info . m_italic ? 1 : 0 ) < < " \" " ;
stream < < SURFACE_TEXT_ATTR < < " = \" " < < ( text_info . m_is_surface_text ? 1 : 0 ) < < " \" " ;
stream < < KEEP_HORIZONTAL_ATTR < < " = \" " < < ( text_info . m_keep_horizontal ? 1 : 0 ) < < " \" " ;
stream < < HIT_MESH_ATTR < < " = \" " < < text_info . m_rr . mesh_id < < " \" " ;
stream < < HIT_POSITION_ATTR < < " = \" " ;
add_vec3 ( stream , text_info . m_rr . hit ) ;
stream < < " \" " ;
stream < < HIT_NORMAL_ATTR < < " = \" " ;
add_vec3 ( stream , text_info . m_rr . normal ) ;
stream < < " \" " ;
stream < < " /> \n " ;
}
bool _QDS_3MF_Exporter : : _add_model_config_file_to_archive ( mz_zip_archive & archive , const Model & model , PlateDataPtrs & plate_data_list , const ObjectToObjectDataMap & objects_data , int export_plate_idx , bool save_gcode , bool use_loaded_id )
{
std : : stringstream stream ;
// 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 ) ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " < " < < CONFIG_TAG < < " > \n " ;
if ( ! m_skip_model )
for ( const ObjectToObjectDataMap : : value_type & obj_metadata : objects_data ) {
auto object_data = obj_metadata . second ;
const ModelObject * obj = object_data . object ;
if ( obj ! = nullptr ) {
// 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";
stream < < " < " < < OBJECT_TAG < < " " < < ID_ATTR < < " = \" " < < object_data . object_id < < " \" > \n " ;
// stores object's name
if ( ! obj - > name . empty ( ) )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" name \" " < < VALUE_ATTR < < " = \" " < < xml_escape ( obj - > name ) < < " \" /> \n " ;
//QDS: store object's module name
if ( ! obj - > module_name . empty ( ) )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" module \" " < < VALUE_ATTR < < " = \" " < < xml_escape ( obj - > module_name ) < < " \" /> \n " ;
// stores object's config data
for ( const std : : string & key : obj - > config . keys ( ) ) {
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < key < < " \" " < < VALUE_ATTR < < " = \" " < < obj - > config . opt_serialize ( key ) < < " \" /> \n " ;
}
for ( const ModelVolume * volume : obj_metadata . second . object - > volumes ) {
if ( volume ! = nullptr ) {
const VolumeToObjectIDMap & objectIDs = obj_metadata . second . volumes_objectID ;
VolumeToObjectIDMap : : const_iterator it = objectIDs . find ( volume ) ;
if ( it ! = objectIDs . end ( ) ) {
// stores volume's offsets
stream < < " < " < < PART_TAG < < " " ;
//stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
//stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\" ";
int volume_id = it - > second ;
if ( m_share_mesh & & volume_id = = 0 )
volume_id = m_volume_paths . find ( volume ) - > second . second ;
stream < < ID_ATTR < < " = \" " < < volume_id < < " \" " ;
stream < < SUBTYPE_ATTR < < " = \" " < < ModelVolume : : type_to_string ( volume - > type ( ) ) < < " \" > \n " ;
//stream << " <" << PART_TAG << " " << ID_ATTR << "=\"" << it->second << "\" " << SUBTYPE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\">\n";
// stores volume's name
if ( ! volume - > name . empty ( ) )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < NAME_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < xml_escape ( volume - > name ) < < " \" /> \n " ;
//stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
// stores volume's modifier field (legacy, to support old slicers)
/*if (volume->is_modifier())
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < PART_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < MODIFIER_KEY < < " \" " < < VALUE_ATTR < < " = \" 1 \" /> \n " ;
// stores volume's type (overrides the modifier field above)
stream < < " < " < < METADATA_TAG < < " " < < TYPE_ATTR < < " = \" " < < PART_TYPE < < " \" " < < KEY_ATTR < < " = \" " < < PART_TYPE_KEY < < " \" " < <
VALUE_ATTR < < " = \" " < < ModelVolume : : type_to_string ( volume - > type ( ) ) < < " \" /> \n " ; */
// stores volume's local matrix
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < MATRIX_KEY < < " \" " < < VALUE_ATTR < < " = \" " ;
Transform3d matrix = volume - > get_matrix ( ) * volume - > source . transform . get_matrix ( ) ;
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
{
std : : string input_file = xml_escape ( m_fullpath_sources ? volume - > source . input_file : boost : : filesystem : : path ( volume - > source . input_file ) . filename ( ) . string ( ) ) ;
//std::string prefix = std::string(" <") + METADATA_TAG + " " + KEY_ATTR + "=\"";
std : : string prefix = std : : string ( " < " ) + METADATA_TAG + " " + KEY_ATTR + " = \" " ;
if ( ! volume - > source . input_file . empty ( ) ) {
stream < < prefix < < SOURCE_FILE_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < input_file < < " \" /> \n " ;
stream < < prefix < < SOURCE_OBJECT_ID_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . object_idx < < " \" /> \n " ;
stream < < prefix < < SOURCE_VOLUME_ID_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . volume_idx < < " \" /> \n " ;
stream < < prefix < < SOURCE_OFFSET_X_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . mesh_offset ( 0 ) < < " \" /> \n " ;
stream < < prefix < < SOURCE_OFFSET_Y_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . mesh_offset ( 1 ) < < " \" /> \n " ;
stream < < prefix < < SOURCE_OFFSET_Z_KEY < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > source . mesh_offset ( 2 ) < < " \" /> \n " ;
}
assert ( ! volume - > source . is_converted_from_inches | | ! volume - > source . is_converted_from_meters ) ;
if ( volume - > source . is_converted_from_inches )
stream < < prefix < < SOURCE_IN_INCHES < < " \" " < < VALUE_ATTR < < " = \" 1 \" /> \n " ;
else if ( volume - > source . is_converted_from_meters )
stream < < prefix < < SOURCE_IN_METERS < < " \" " < < VALUE_ATTR < < " = \" 1 \" /> \n " ;
}
// stores volume's config data
for ( const std : : string & key : volume - > config . keys ( ) ) {
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < key < < " \" " < < VALUE_ATTR < < " = \" " < < volume - > config . opt_serialize ( key ) < < " \" /> \n " ;
}
const TextInfo & text_info = volume - > get_text_info ( ) ;
if ( ! text_info . m_text . empty ( ) )
_add_text_info_to_archive ( stream , text_info ) ;
// stores mesh's statistics
const RepairedMeshErrors & stats = volume - > mesh ( ) . stats ( ) . repaired_errors ;
stream < < " < " < < MESH_STAT_TAG < < " " ;
stream < < MESH_STAT_EDGES_FIXED < < " = \" " < < stats . edges_fixed < < " \" " ;
stream < < MESH_STAT_DEGENERATED_FACETS < < " = \" " < < stats . degenerate_facets < < " \" " ;
stream < < MESH_STAT_FACETS_REMOVED < < " = \" " < < stats . facets_removed < < " \" " ;
stream < < MESH_STAT_FACETS_RESERVED < < " = \" " < < stats . facets_reversed < < " \" " ;
stream < < MESH_STAT_BACKWARDS_EDGES < < " = \" " < < stats . backwards_edges < < " \" /> \n " ;
stream < < " </ " < < PART_TAG < < " > \n " ;
}
}
}
stream < < " </ " < < OBJECT_TAG < < " > \n " ;
}
}
//QDS: store plate related logic
std : : vector < std : : string > gcode_paths ;
for ( unsigned int i = 0 ; i < ( unsigned int ) plate_data_list . size ( ) ; + + i )
{
PlateData * plate_data = plate_data_list [ i ] ;
int instance_size = plate_data - > objects_and_instances . size ( ) ;
if ( plate_data ! = nullptr ) {
stream < < " < " < < PLATE_TAG < < " > \n " ;
//plate index
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PLATERID_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < plate_data - > plate_index + 1 < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PLATER_NAME_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < xml_escape ( plate_data - > plate_name . c_str ( ) ) < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < LOCK_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < plate_data - > locked < < " \" /> \n " ;
ConfigOption * bed_type_opt = plate_data - > config . option ( " curr_bed_type " ) ;
t_config_enum_names bed_type_names = ConfigOptionEnum < BedType > : : get_enum_names ( ) ;
if ( bed_type_opt ! = nullptr & & bed_type_names . size ( ) > bed_type_opt - > getInt ( ) )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < BED_TYPE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < bed_type_names [ bed_type_opt - > getInt ( ) ] < < " \" /> \n " ;
ConfigOption * print_sequence_opt = plate_data - > config . option ( " print_sequence " ) ;
t_config_enum_names print_sequence_names = ConfigOptionEnum < PrintSequence > : : get_enum_names ( ) ;
if ( print_sequence_opt ! = nullptr & & print_sequence_names . size ( ) > print_sequence_opt - > getInt ( ) )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PRINT_SEQUENCE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < print_sequence_names [ print_sequence_opt - > getInt ( ) ] < < " \" /> \n " ;
ConfigOptionInts * first_layer_print_sequence_opt = plate_data - > config . option < ConfigOptionInts > ( " first_layer_print_sequence " ) ;
if ( first_layer_print_sequence_opt ! = nullptr ) {
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < FIRST_LAYER_PRINT_SEQUENCE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " ;
const std : : vector < int > & values = first_layer_print_sequence_opt - > values ;
for ( int i = 0 ; i < values . size ( ) ; + + i ) {
stream < < values [ i ] ;
if ( i ! = ( values . size ( ) - 1 ) )
stream < < " " ;
}
stream < < " \" /> \n " ;
}
ConfigOptionInts * other_layers_print_sequence_opt = plate_data - > config . option < ConfigOptionInts > ( " other_layers_print_sequence " ) ;
if ( other_layers_print_sequence_opt ! = nullptr ) {
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < OTHER_LAYERS_PRINT_SEQUENCE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " ;
const std : : vector < int > & values = other_layers_print_sequence_opt - > values ;
for ( int i = 0 ; i < values . size ( ) ; + + i ) {
stream < < values [ i ] ;
if ( i ! = ( values . size ( ) - 1 ) )
stream < < " " ;
}
stream < < " \" /> \n " ;
}
const ConfigOptionInt * sequence_nums_opt = dynamic_cast < const ConfigOptionInt * > ( plate_data - > config . option ( " other_layers_print_sequence_nums " ) ) ;
if ( sequence_nums_opt ! = nullptr ) {
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < sequence_nums_opt - > getInt ( ) < < " \" /> \n " ;
}
ConfigOption * spiral_mode_opt = plate_data - > config . option ( " spiral_mode " ) ;
if ( spiral_mode_opt )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < SPIRAL_VASE_MODE < < " \" " < < VALUE_ATTR < < " = \" " < < spiral_mode_opt - > getBool ( ) < < " \" /> \n " ;
if ( save_gcode )
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < GCODE_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < xml_escape ( plate_data - > gcode_file ) < < " \" /> \n " ;
if ( ! plate_data - > gcode_file . empty ( ) ) {
gcode_paths . push_back ( plate_data - > gcode_file ) ;
}
if ( plate_data - > plate_thumbnail . is_valid ( ) ) {
std : : string thumbnail_file_in_3mf = ( boost : : format ( THUMBNAIL_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < THUMBNAIL_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < thumbnail_file_in_3mf < < " \" /> \n " ;
}
else if ( ! plate_data - > thumbnail_file . empty ( ) & & ( boost : : filesystem : : exists ( plate_data - > thumbnail_file ) ) ) {
std : : string thumbnail_file_in_3mf = ( boost : : format ( THUMBNAIL_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < THUMBNAIL_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < thumbnail_file_in_3mf < < " \" /> \n " ;
}
if ( ! plate_data - > no_light_thumbnail_file . empty ( ) ) {
std : : string no_light_thumbnail_file_in_3mf = ( boost : : format ( NO_LIGHT_THUMBNAIL_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < NO_LIGHT_THUMBNAIL_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < no_light_thumbnail_file_in_3mf < < " \" /> \n " ;
}
if ( ! plate_data - > top_file . empty ( ) ) {
std : : string top_file_in_3mf = ( boost : : format ( TOP_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < TOP_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < top_file_in_3mf < < " \" /> \n " ;
}
if ( ! plate_data - > pick_file . empty ( ) ) {
std : : string pick_file_in_3mf = ( boost : : format ( PICK_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PICK_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < pick_file_in_3mf < < " \" /> \n " ;
}
/*if (!plate_data->pattern_file.empty()) {
std : : string pattern_file_in_3mf = ( boost : : format ( PATTERN_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PATTERN_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < pattern_file_in_3mf < < " \" /> \n " ;
} */
if ( ! plate_data - > pattern_bbox_file . empty ( ) ) {
std : : string pattern_bbox_file_in_3mf = ( boost : : format ( PATTERN_CONFIG_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PATTERN_BBOX_FILE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < pattern_bbox_file_in_3mf < < " \" /> \n " ;
}
if ( ! m_skip_model & & instance_size > 0 )
{
for ( unsigned int j = 0 ; j < instance_size ; + + j )
{
stream < < " < " < < INSTANCE_TAG < < " > \n " ;
int obj_id = plate_data - > objects_and_instances [ j ] . first ;
int inst_id = plate_data - > objects_and_instances [ j ] . second ;
int identify_id = 0 ;
ModelObject * obj = NULL ;
ModelInstance * inst = NULL ;
if ( obj_id > = model . objects . size ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " invalid object id %1% \n " ) % obj_id ;
}
else
obj = model . objects [ obj_id ] ;
if ( obj & & ( inst_id > = obj - > instances . size ( ) ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " invalid instance id %1% \n " ) % inst_id ;
}
else if ( obj ) {
inst = obj - > instances [ inst_id ] ;
if ( use_loaded_id & & ( inst - > loaded_id > 0 ) )
2024-09-16 16:07:29 +08:00
{
//1.9.5
2024-09-03 09:34:33 +08:00
identify_id = inst - > loaded_id ;
2024-09-16 16:07:29 +08:00
if ( identify_id & 0xFF000000 ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , identify_id %1%, too big, limit the high bits to 0 \n " ) % identify_id ;
identify_id = identify_id & 0x00FFFFFF ;
}
}
2024-09-03 09:34:33 +08:00
else
identify_id = inst - > id ( ) . id ;
}
obj_id = objects_data . find ( obj ) - > second . object_id ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < OBJECT_ID_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < obj_id < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < INSTANCEID_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < inst_id < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < IDENTIFYID_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < identify_id < < " \" /> \n " ;
stream < < " </ " < < INSTANCE_TAG < < " > \n " ;
}
}
stream < < " </ " < < PLATE_TAG < < " > \n " ;
}
}
// write model rels
if ( save_gcode )
_add_relationships_file_to_archive ( archive , QDS_MODEL_CONFIG_RELS_FILE , gcode_paths , { " http://schemas.qidilab.com/package/2021/gcode " } , Slic3r : : PackingTemporaryData ( ) , export_plate_idx ) ;
if ( ! m_skip_model ) {
//QDS: store assemble related info
stream < < " < " < < ASSEMBLE_TAG < < " > \n " ;
for ( const ObjectToObjectDataMap : : value_type & obj_metadata : objects_data ) {
auto object_data = obj_metadata . second ;
const ModelObject * obj = object_data . object ;
if ( obj ! = nullptr ) {
for ( int instance_idx = 0 ; instance_idx < obj - > instances . size ( ) ; + + instance_idx ) {
if ( obj - > instances [ instance_idx ] - > is_assemble_initialized ( ) ) {
stream < < " < " < < ASSEMBLE_ITEM_TAG < < " " < < OBJECT_ID_ATTR < < " = \" " < < object_data . object_id < < " \" " ;
stream < < INSTANCEID_ATTR < < " = \" " < < instance_idx < < " \" " < < TRANSFORM_ATTR < < " = \" " ;
for ( unsigned c = 0 ; c < 4 ; + + c ) {
for ( unsigned r = 0 ; r < 3 ; + + r ) {
const Transform3d assemble_trans = obj - > instances [ instance_idx ] - > get_assemble_transformation ( ) . get_matrix ( ) ;
stream < < assemble_trans ( r , c ) ;
if ( r ! = 2 | | c ! = 3 )
stream < < " " ;
}
}
stream < < " \" " ;
stream < < OFFSET_ATTR < < " = \" " ;
const Vec3d ofs2ass = obj - > instances [ instance_idx ] - > get_offset_to_assembly ( ) ;
stream < < ofs2ass ( 0 ) < < " " < < ofs2ass ( 1 ) < < " " < < ofs2ass ( 2 ) ;
stream < < " \" /> \n " ;
}
}
}
}
stream < < " </ " < < ASSEMBLE_TAG < < " > \n " ;
}
stream < < " </ " < < CONFIG_TAG < < " > \n " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , QDS_MODEL_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " Unable to add model config file to archive \n " ) ;
add_error ( " Unable to add model config file to archive " ) ;
return false ;
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_cut_information_file_to_archive ( mz_zip_archive & archive , Model & model )
{
std : : string out = " " ;
pt : : ptree tree ;
unsigned int object_cnt = 0 ;
for ( const ModelObject * object : model . objects ) {
object_cnt + + ;
pt : : ptree & obj_tree = tree . add ( " objects.object " , " " ) ;
obj_tree . put ( " <xmlattr>.id " , object_cnt ) ;
// Store info for cut_id
pt : : ptree & cut_id_tree = obj_tree . add ( " cut_id " , " " ) ;
// store cut_id atributes
cut_id_tree . put ( " <xmlattr>.id " , object - > cut_id . id ( ) . id ) ;
cut_id_tree . put ( " <xmlattr>.check_sum " , object - > cut_id . check_sum ( ) ) ;
cut_id_tree . put ( " <xmlattr>.connectors_cnt " , object - > cut_id . connectors_cnt ( ) ) ;
int volume_idx = - 1 ;
for ( const ModelVolume * volume : object - > volumes ) {
+ + volume_idx ;
if ( volume - > is_cut_connector ( ) ) {
pt : : ptree & connectors_tree = obj_tree . add ( " connectors.connector " , " " ) ;
connectors_tree . put ( " <xmlattr>.volume_id " , volume_idx ) ;
connectors_tree . put ( " <xmlattr>.type " , int ( volume - > cut_info . connector_type ) ) ;
connectors_tree . put ( " <xmlattr>.radius " , volume - > cut_info . radius ) ;
connectors_tree . put ( " <xmlattr>.height " , volume - > cut_info . height ) ;
connectors_tree . put ( " <xmlattr>.r_tolerance " , volume - > cut_info . radius_tolerance ) ;
connectors_tree . put ( " <xmlattr>.h_tolerance " , volume - > cut_info . height_tolerance ) ;
}
}
}
if ( ! tree . empty ( ) ) {
std : : ostringstream oss ;
pt : : write_xml ( oss , tree ) ;
out = oss . str ( ) ;
// Post processing("beautification") of the output string for a better preview
boost : : replace_all ( out , " ><object " , " > \n <object " ) ;
boost : : replace_all ( out , " ><cut_id " , " > \n <cut_id " ) ;
boost : : replace_all ( out , " ></cut_id> " , " > \n </cut_id> " ) ;
boost : : replace_all ( out , " ><connectors " , " > \n <connectors " ) ;
boost : : replace_all ( out , " ></connectors> " , " > \n </connectors> " ) ;
boost : : replace_all ( out , " ><connector " , " > \n <connector " ) ;
boost : : replace_all ( out , " ></connector> " , " > \n </connector> " ) ;
boost : : replace_all ( out , " ></object> " , " > \n </object> " ) ;
// OR just
boost : : replace_all ( out , " >< " , " > \n < " ) ;
}
if ( ! out . empty ( ) ) {
if ( ! mz_zip_writer_add_mem ( & archive , CUT_INFORMATION_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add cut information file to archive " ) ;
return false ;
}
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_slice_info_config_file_to_archive ( mz_zip_archive & archive , const Model & model , PlateDataPtrs & plate_data_list , const ObjectToObjectDataMap & objects_data , const DynamicPrintConfig & config )
{
std : : stringstream stream ;
// 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 ) ;
stream < < std : : setiosflags ( std : : ios : : fixed ) < < std : : setprecision ( 2 ) ;
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " < " < < CONFIG_TAG < < " > \n " ;
// save slice header for debug
stream < < " < " < < SLICE_HEADER_TAG < < " > \n " ;
stream < < " < " < < SLICE_HEADER_ITEM_TAG < < " " < < KEY_ATTR < < " = \" " < < " X-QDT-Client-Type " < < " \" " < < VALUE_ATTR < < " = \" " < < " slicer " < < " \" /> \n " ;
stream < < " < " < < SLICE_HEADER_ITEM_TAG < < " " < < KEY_ATTR < < " = \" " < < " X-QDT-Client-Version " < < " \" " < < VALUE_ATTR < < " = \" " < < convert_to_full_version ( SLIC3R_VERSION ) < < " \" /> \n " ;
stream < < " </ " < < SLICE_HEADER_TAG < < " > \n " ;
for ( unsigned int i = 0 ; i < ( unsigned int ) plate_data_list . size ( ) ; + + i )
{
PlateData * plate_data = plate_data_list [ i ] ;
//int instance_size = plate_data->objects_and_instances.size();
if ( plate_data ! = nullptr & & plate_data - > is_sliced_valid ) {
stream < < " < " < < PLATE_TAG < < " > \n " ;
//plate index
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PLATE_IDX_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < plate_data - > plate_index + 1 < < " \" /> \n " ;
int timelapse_type = int ( config . opt_enum < TimelapseType > ( " timelapse_type " ) ) ;
for ( auto it = plate_data - > warnings . begin ( ) ; it ! = plate_data - > warnings . end ( ) ; it + + ) {
if ( it - > msg = = NOT_GENERATE_TIMELAPSE ) {
timelapse_type = - 1 ;
break ;
}
}
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < PRINTER_MODEL_ID_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < plate_data - > printer_model_id < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < NOZZLE_DIAMETERS_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < plate_data - > nozzle_diameters < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < TIMELAPSE_TYPE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < timelapse_type < < " \" /> \n " ;
//stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TIMELAPSE_ERROR_CODE_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->timelapse_warning_code << "\"/>\n";
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < SLICE_PREDICTION_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < plate_data - > get_gcode_prediction_str ( ) < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < SLICE_WEIGHT_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < plate_data - > get_gcode_weight_str ( ) < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < OUTSIDE_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < plate_data - > toolpath_outside < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < SUPPORT_USED_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < plate_data - > is_support_used < < " \" /> \n " ;
stream < < " < " < < METADATA_TAG < < " " < < KEY_ATTR < < " = \" " < < LABEL_OBJECT_ENABLED_ATTR < < " \" " < < VALUE_ATTR < < " = \" " < < std : : boolalpha < < plate_data - > is_label_object_enabled < < " \" /> \n " ;
for ( auto it = plate_data - > objects_and_instances . begin ( ) ; it ! = plate_data - > objects_and_instances . end ( ) ; it + + )
{
int obj_id = it - > first ;
int inst_id = it - > second ;
int identify_id = 0 ;
ModelObject * obj = NULL ;
ModelInstance * inst = NULL ;
if ( obj_id > = model . objects . size ( ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " invalid object id %1% \n " ) % obj_id ;
continue ;
}
obj = model . objects [ obj_id ] ;
if ( obj & & ( inst_id > = obj - > instances . size ( ) ) ) {
BOOST_LOG_TRIVIAL ( warning ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " invalid instance id %1% \n " ) % inst_id ;
continue ;
}
inst = obj - > instances [ inst_id ] ;
2024-09-16 16:07:29 +08:00
//1.9.5
if ( ! inst - > printable )
continue ;
2024-09-03 09:34:33 +08:00
if ( m_use_loaded_id & & ( inst - > loaded_id > 0 ) )
2024-09-16 16:07:29 +08:00
{
2024-09-03 09:34:33 +08:00
identify_id = inst - > loaded_id ;
2024-09-16 16:07:29 +08:00
if ( identify_id & 0xFF000000 ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , identify_id %1%, too big, limit the high bits to 0 \n " ) % identify_id ;
identify_id = identify_id & 0x00FFFFFF ;
}
}
2024-09-03 09:34:33 +08:00
else
identify_id = inst - > id ( ) . id ;
bool skipped = std : : find ( plate_data - > skipped_objects . begin ( ) , plate_data - > skipped_objects . end ( ) , identify_id ) ! =
plate_data - > skipped_objects . end ( ) ;
stream < < " < " < < OBJECT_TAG < < " " < < IDENTIFYID_ATTR < < " = \" " < < std : : to_string ( identify_id ) < < " \" " < < NAME_ATTR < < " = \" " < < xml_escape ( obj - > name )
< < " \" " < < SKIPPED_ATTR < < " = \" " < < ( skipped ? " true " : " false " )
< < " \" /> \n " ;
}
for ( auto it = plate_data - > slice_filaments_info . begin ( ) ; it ! = plate_data - > slice_filaments_info . end ( ) ; it + + )
{
stream < < " < " < < FILAMENT_TAG < < " " < < FILAMENT_ID_TAG < < " = \" " < < std : : to_string ( it - > id + 1 ) < < " \" "
< < FILAMENT_TRAY_INFO_ID_TAG < < " = \" " < < it - > filament_id < < " \" "
< < FILAMENT_TYPE_TAG < < " = \" " < < it - > type < < " \" "
< < FILAMENT_COLOR_TAG < < " = \" " < < it - > color < < " \" "
< < FILAMENT_USED_M_TAG < < " = \" " < < it - > used_m < < " \" "
< < FILAMENT_USED_G_TAG < < " = \" " < < it - > used_g < < " \" /> \n " ;
}
for ( auto it = plate_data - > warnings . begin ( ) ; it ! = plate_data - > warnings . end ( ) ; it + + ) {
stream < < " < " < < SLICE_WARNING_TAG < < " msg= \" " < < it - > msg < < " \" level= \" " < < std : : to_string ( it - > level ) < < " \" error_code = \" " < < it - > error_code < < " \" /> \n " ;
}
stream < < " </ " < < PLATE_TAG < < " > \n " ;
}
}
stream < < " </ " < < CONFIG_TAG < < " > \n " ;
std : : string out = stream . str ( ) ;
if ( ! mz_zip_writer_add_mem ( & archive , SLICE_INFO_CONFIG_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add model config file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , store slice-info to 3mf, length %1%, failed \n " ) % out . length ( ) ;
return false ;
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_gcode_file_to_archive ( mz_zip_archive & archive , const Model & model , PlateDataPtrs & plate_data_list , Export3mfProgressFn proFn )
{
bool result = true ;
bool cb_cancel = false ;
PlateDataPtrs plate_data_list2 ;
for ( unsigned int i = 0 ; i < ( unsigned int ) plate_data_list . size ( ) ; + + i )
{
if ( proFn ) {
proFn ( EXPORT_STAGE_ADD_GCODE , i , plate_data_list . size ( ) , cb_cancel ) ;
if ( cb_cancel )
return false ;
}
PlateData * plate_data = plate_data_list [ i ] ;
if ( ! plate_data - > gcode_file . empty ( ) & & plate_data - > is_sliced_valid & & boost : : filesystem : : exists ( plate_data - > gcode_file ) ) {
plate_data_list2 . push_back ( plate_data ) ;
}
}
boost : : mutex mutex ;
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , plate_data_list2 . size ( ) , 1 ) , [ this , & plate_data_list2 , & root_archive = archive , & mutex , & result ] ( const tbb : : blocked_range < size_t > & range ) {
for ( int i = range . begin ( ) ; i < range . end ( ) ; + + i ) {
PlateData * plate_data = plate_data_list2 [ i ] ;
auto src_gcode_file = plate_data - > gcode_file ;
std : : string gcode_in_3mf = ( boost : : format ( GCODE_FILE_FORMAT ) % ( plate_data - > plate_index + 1 ) ) . str ( ) ;
plate_data - > gcode_file = gcode_in_3mf ;
mz_zip_archive archive ;
mz_zip_writer_staged_context context ;
mz_zip_zero_struct ( & archive ) ;
mz_zip_writer_init_heap ( & archive , 0 , 1024 * 1024 ) ;
{
mz_zip_writer_add_staged_open ( & archive , & context , gcode_in_3mf . c_str ( ) , m_zip64 ? ( uint64_t ( 1 ) < < 30 ) * 16 : ( uint64_t ( 1 ) < < 32 ) - 1 , nullptr , nullptr , 0 ,
MZ_DEFAULT_COMPRESSION , nullptr , 0 , nullptr , 0 ) ;
boost : : filesystem : : path src_gcode_path ( src_gcode_file ) ;
if ( ! boost : : filesystem : : exists ( src_gcode_path ) ) {
BOOST_LOG_TRIVIAL ( error ) < < " Gcode is missing, filename = " < < src_gcode_file ;
result = false ;
}
boost : : filesystem : : ifstream ifs ( src_gcode_file , std : : ios : : binary ) ;
std : : string buf ( 64 * 1024 , 0 ) ;
while ( ifs ) {
ifs . read ( buf . data ( ) , buf . size ( ) ) ;
mz_zip_writer_add_staged_data ( & context , buf . data ( ) , ifs . gcount ( ) ) ;
}
mz_zip_writer_add_staged_finish ( & context ) ;
}
void * ppBuf ; size_t pSize ;
mz_zip_writer_finalize_heap_archive ( & archive , & ppBuf , & pSize ) ;
mz_zip_writer_end ( & archive ) ;
mz_zip_zero_struct ( & archive ) ;
mz_zip_reader_init_mem ( & archive , ppBuf , pSize , 0 ) ;
{
boost : : unique_lock l ( mutex ) ;
mz_zip_writer_add_from_zip_reader ( & root_archive , & archive , 0 ) ;
}
mz_zip_reader_end ( & archive ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , store %1% to 3mf %2% \n " ) % src_gcode_file % gcode_in_3mf ;
}
} ) ;
return result ;
}
bool _QDS_3MF_Exporter : : _add_custom_gcode_per_print_z_file_to_archive ( mz_zip_archive & archive , Model & model , const DynamicPrintConfig * config )
{
//QDS: add plate tree related logic
std : : string out = " " ;
bool has_custom_gcode = false ;
pt : : ptree tree ;
pt : : ptree & main_tree = tree . add ( " custom_gcodes_per_layer " , " " ) ;
for ( auto custom_gcodes : model . plates_custom_gcodes ) {
has_custom_gcode = true ;
pt : : ptree & plate_tree = main_tree . add ( " plate " , " " ) ;
pt : : ptree & plate_idx_tree = plate_tree . add ( " plate_info " , " " ) ;
plate_idx_tree . put ( " <xmlattr>.id " , custom_gcodes . first + 1 ) ;
// store data of custom_gcode_per_print_z
for ( const CustomGCode : : Item & code : custom_gcodes . second . gcodes ) {
pt : : ptree & code_tree = plate_tree . add ( " layer " , " " ) ;
code_tree . put ( " <xmlattr>.top_z " , code . print_z ) ;
code_tree . put ( " <xmlattr>.type " , static_cast < int > ( code . type ) ) ;
code_tree . put ( " <xmlattr>.extruder " , code . extruder ) ;
code_tree . put ( " <xmlattr>.color " , code . color ) ;
code_tree . put ( " <xmlattr>.extra " , code . extra ) ;
//QDS
std : : string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
code . type = = CustomGCode : : PausePrint ? config - > opt_string ( " machine_pause_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 ) ;
}
pt : : ptree & mode_tree = plate_tree . add ( " mode " , " " ) ;
// store mode of a custom_gcode_per_print_z
mode_tree . put ( " <xmlattr>.value " , custom_gcodes . second . mode = = CustomGCode : : Mode : : SingleExtruder ? CustomGCode : : SingleExtruderMode :
custom_gcodes . second . mode = = CustomGCode : : Mode : : MultiAsSingle ? CustomGCode : : MultiAsSingleMode :
CustomGCode : : MultiExtruderMode ) ;
}
if ( has_custom_gcode ) {
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 ( ) ) {
if ( ! mz_zip_writer_add_mem ( & archive , CUSTOM_GCODE_PER_PRINT_Z_FILE . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) ) {
add_error ( " Unable to add custom Gcodes per print_z file to archive " ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " : " < < __LINE__ < < boost : : format ( " , Unable to add custom Gcodes per print_z file to archive \n " ) ;
return false ;
}
}
return true ;
}
bool _QDS_3MF_Exporter : : _add_auxiliary_dir_to_archive ( mz_zip_archive & archive , const std : : string & aux_dir , PackingTemporaryData & data )
{
bool result = true ;
if ( aux_dir . empty ( ) ) {
//no accessory directories
return result ;
}
boost : : filesystem : : path dir = boost : : filesystem : : path ( aux_dir ) ;
if ( ! boost : : filesystem : : exists ( dir ) )
{
//no accessory directories
return result ;
}
static std : : string const nocomp_exts [ ] = { " .png " , " .jpg " , " .mp4 " , " .jpeg " } ;
std : : deque < boost : : filesystem : : path > directories ( { dir } ) ;
int root_dir_len = dir . string ( ) . length ( ) + 1 ;
//boost file access
while ( ! directories . empty ( ) ) {
boost : : system : : error_code ec ;
boost : : filesystem : : directory_iterator iterator ( directories . front ( ) , ec ) ;
directories . pop_front ( ) ;
if ( ec ) continue ;
for ( ; iterator ! = end ( iterator ) ; iterator . increment ( ec ) )
{
if ( ec ) break ;
auto dir_entry = * iterator ;
std : : string src_file ;
std : : string dst_in_3mf ;
if ( boost : : filesystem : : is_directory ( dir_entry . path ( ) , ec ) )
{
directories . push_back ( dir_entry . path ( ) ) ;
continue ;
}
if ( boost : : filesystem : : is_regular_file ( dir_entry . path ( ) , ec ) & & ! m_skip_auxiliary )
{
src_file = dir_entry . path ( ) . string ( ) ;
dst_in_3mf = dir_entry . path ( ) . string ( ) ;
dst_in_3mf . replace ( 0 , root_dir_len , AUXILIARY_DIR ) ;
std : : replace ( dst_in_3mf . begin ( ) , dst_in_3mf . end ( ) , ' \\ ' , ' / ' ) ;
if ( _3MF_COVER_FILE . compare ( 1 , _3MF_COVER_FILE . length ( ) - 1 , dst_in_3mf ) = = 0 ) {
data . _3mf_thumbnail = dst_in_3mf ;
} else if ( m_thumbnail_small . compare ( 1 , m_thumbnail_small . length ( ) - 1 , dst_in_3mf ) = = 0 ) {
data . _3mf_printer_thumbnail_small = dst_in_3mf ;
if ( m_thumbnail_middle = = m_thumbnail_small ) data . _3mf_printer_thumbnail_middle = dst_in_3mf ;
} else if ( m_thumbnail_middle . compare ( 1 , m_thumbnail_middle . length ( ) - 1 , dst_in_3mf ) = = 0 ) {
data . _3mf_printer_thumbnail_middle = dst_in_3mf ;
}
result & = _add_file_to_archive ( archive , dst_in_3mf , src_file ) ;
}
}
}
return result ;
}
// Perform conversions based on the config values available.
//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
static void handle_legacy_project_loaded ( unsigned int version_project_file , DynamicPrintConfig & config )
{
if ( ! config . has ( " brim_object_gap " ) ) {
if ( auto * opt_elephant_foot = config . option < ConfigOptionFloat > ( " elefant_foot_compensation " , false ) ; opt_elephant_foot ) {
// Conversion from older PrusaSlicer which applied brim separation equal to elephant foot compensation.
auto * opt_brim_separation = config . option < ConfigOptionFloat > ( " brim_object_gap " , true ) ;
opt_brim_separation - > value = opt_elephant_foot - > value ;
}
}
}
// backup backgroud thread to dispatch tasks and coperate with ui thread
class _QDS_Backup_Manager
{
public :
static _QDS_Backup_Manager & get ( ) {
static _QDS_Backup_Manager m ;
return m ;
}
void set_post_callback ( std : : function < void ( int ) > c ) {
boost : : lock_guard lock ( m_mutex ) ;
m_post_callback = c ;
}
void run_ui_tasks ( ) {
std : : deque < Task > tasks ;
{
boost : : lock_guard lock ( m_mutex ) ;
std : : swap ( tasks , m_ui_tasks ) ;
}
for ( auto & t : tasks )
{
process_ui_task ( t ) ;
}
}
void push_object_gaurd ( ModelObject & object ) {
m_gaurd_objects . push_back ( std : : make_pair ( & object , 0 ) ) ;
}
void pop_object_gaurd ( ) {
auto object = m_gaurd_objects . back ( ) ;
m_gaurd_objects . pop_back ( ) ;
if ( object . second )
add_object_mesh ( * object . first ) ;
}
void add_object_mesh ( ModelObject & object ) {
for ( auto & g : m_gaurd_objects ) {
if ( g . first = = & object ) {
+ + g . second ;
return ;
}
}
// clone object
auto model = object . get_model ( ) ;
auto o = m_temp_model . add_object ( object ) ;
int backup_id = model - > get_object_backup_id ( object ) ;
push_task ( { AddObject , ( size_t ) backup_id , object . get_model ( ) - > get_backup_path ( ) , o , 1 } ) ;
}
void remove_object_mesh ( ModelObject & object ) {
push_task ( { RemoveObject , object . id ( ) . id , object . get_model ( ) - > get_backup_path ( ) } ) ;
}
void backup_soon ( ) {
boost : : lock_guard lock ( m_mutex ) ;
m_other_changes_backup = true ;
m_tasks . push_back ( { Backup , 0 , std : : string ( ) , nullptr , + + m_task_seq } ) ;
m_cond . notify_all ( ) ;
}
void remove_backup ( Model & model , bool removeAll ) {
BOOST_LOG_TRIVIAL ( info )
< < " remove_backup " < < model . get_backup_path ( ) < < " , " < < removeAll ;
std : : deque < Task > canceled_tasks ;
boost : : unique_lock lock ( m_mutex ) ;
if ( removeAll & & model . is_need_backup ( ) ) {
// running task may not be canceled
for ( auto & t : m_ui_tasks )
canceled_tasks . push_back ( t ) ;
for ( auto & t : m_tasks )
canceled_tasks . push_back ( t ) ;
m_ui_tasks . clear ( ) ;
m_tasks . clear ( ) ;
}
m_tasks . push_back ( { RemoveBackup , model . id ( ) . id , model . get_backup_path ( ) , nullptr , removeAll } ) ;
+ + m_task_seq ;
if ( model . is_need_backup ( ) ) {
m_other_changes = false ;
m_other_changes_backup = false ;
}
m_cond . notify_all ( ) ;
lock . unlock ( ) ;
for ( auto & t : canceled_tasks ) {
process_ui_task ( t , true ) ;
}
}
void set_interval ( long n ) {
boost : : lock_guard lock ( m_mutex ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " entry, and last interval is: " < < m_interval ;
m_next_backup - = boost : : posix_time : : seconds ( m_interval ) ;
m_interval = n ;
m_next_backup + = boost : : posix_time : : seconds ( m_interval ) ;
m_cond . notify_all ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " exit, and new interval is: " < < m_interval ;
}
void put_other_changes ( )
{
BOOST_LOG_TRIVIAL ( info ) < < " put_other_changes " ;
m_other_changes = true ;
m_other_changes_backup = true ;
}
void clear_other_changes ( bool backup )
{
if ( backup )
m_other_changes_backup = false ;
else
m_other_changes = false ;
}
bool has_other_changes ( bool backup )
{
return backup ? m_other_changes_backup : m_other_changes ;
}
private :
enum TaskType {
None ,
Backup , // this task is working as response in ui thread
AddObject ,
RemoveObject ,
RemoveBackup ,
Exit
} ;
struct Task {
TaskType type ;
size_t id = 0 ;
std : : string path ;
ModelObject * object = nullptr ;
union {
size_t delay = 0 ; // delay sequence, only last task is delayed
size_t sequence ;
bool removeAll ;
} ;
friend bool operator = = ( Task const & l , Task const & r ) {
return l . type = = r . type & & l . id = = r . id ;
}
std : : string to_string ( ) const {
constexpr char const * type_names [ ] = { " None " ,
" Backup " ,
" AddObject " ,
" RemoveObject " ,
" RemoveBackup " ,
" Exit " } ;
std : : ostringstream os ;
os < < " { type: " < < type_names [ type ] < < " , id: " < < id
< < " , path: " < < path
< < " , object: " < < ( object ? object - > id ( ) . id : 0 ) < < " , extra: " < < delay < < " } " ;
return os . str ( ) ;
}
} ;
struct timer {
timer ( char const * msg ) : msg ( msg ) , start ( boost : : posix_time : : microsec_clock : : universal_time ( ) ) { }
~ timer ( ) {
# ifdef __WIN32__
auto end = boost : : posix_time : : microsec_clock : : universal_time ( ) ;
int duration = ( int ) ( end - start ) . total_milliseconds ( ) ;
char buf [ 20 ] ;
OutputDebugStringA ( msg ) ;
OutputDebugStringA ( " : " ) ;
OutputDebugStringA ( itoa ( duration , buf , 10 ) ) ;
OutputDebugStringA ( " \n " ) ;
# endif
}
char const * msg ;
boost : : posix_time : : ptime start ;
} ;
private :
_QDS_Backup_Manager ( ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " inital and interval = " < < m_interval ;
m_next_backup = boost : : get_system_time ( ) + boost : : posix_time : : seconds ( m_interval ) ;
boost : : unique_lock lock ( m_mutex ) ;
m_thread = std : : move ( boost : : thread ( boost : : ref ( * this ) ) ) ;
}
~ _QDS_Backup_Manager ( ) {
push_task ( { Exit } ) ;
m_thread . join ( ) ;
}
void push_task ( Task const & t ) {
boost : : unique_lock lock ( m_mutex ) ;
if ( t . delay & & ! m_tasks . empty ( ) & & m_tasks . back ( ) = = t ) {
auto t2 = m_tasks . back ( ) ;
m_tasks . back ( ) = t ;
m_tasks . back ( ) . delay = t2 . delay + 1 ;
m_cond . notify_all ( ) ;
lock . unlock ( ) ;
process_ui_task ( t2 ) ;
}
else {
m_tasks . push_back ( t ) ;
+ + m_task_seq ;
m_cond . notify_all ( ) ;
}
}
void process_ui_task ( Task & t , bool canceled = false ) {
switch ( t . type ) {
case Backup : {
if ( canceled )
break ;
std : : function < void ( int ) > callback ;
boost : : unique_lock lock ( m_mutex ) ;
if ( m_task_seq ! = t . sequence ) {
if ( find ( m_tasks . begin ( ) , m_tasks . end ( ) , Task { Backup } ) = = m_tasks . end ( ) ) {
t . sequence = + + m_task_seq ; // may has pending tasks, retry later
m_tasks . push_back ( t ) ;
m_cond . notify_all ( ) ;
}
break ;
}
callback = m_post_callback ;
lock . unlock ( ) ;
{
timer t ( " backup cost " ) ;
try {
if ( callback ) callback ( 1 ) ;
} catch ( . . . ) { }
}
m_other_changes_backup = false ;
break ;
}
case AddObject :
m_temp_model . delete_object ( t . object ) ;
break ;
case RemoveBackup :
if ( t . removeAll ) {
try {
boost : : filesystem : : remove ( t . path + " /lock.txt " ) ;
boost : : filesystem : : remove_all ( t . path ) ;
BOOST_LOG_TRIVIAL ( info ) < < " process_ui_task: remove all of backup path " < < t . path ;
} catch ( std : : exception & ex ) {
BOOST_LOG_TRIVIAL ( error ) < < " process_ui_task: failed to remove backup path " < < t . path < < " : " < < ex . what ( ) ;
}
}
break ;
}
}
void process_task ( Task & t ) {
switch ( t . type ) {
case Backup :
// do it in response
break ;
case AddObject : {
BOOST_LOG_TRIVIAL ( info ) < < " process_task " < < t . to_string ( ) ;
{
CNumericLocalesSetter locales_setter ;
_QDS_3MF_Exporter e ;
e . save_object_mesh ( t . path , * t . object , ( int ) t . id ) ;
// response to delete cloned object
}
break ;
}
case RemoveObject : {
BOOST_LOG_TRIVIAL ( info ) < < " process_task " < < t . to_string ( ) ;
boost : : system : : error_code ec ;
boost : : filesystem : : remove ( t . path + " /mesh_ " + boost : : lexical_cast < std : : string > ( t . id ) + " .xml " , ec ) ;
t . type = None ;
break ;
}
case RemoveBackup : {
BOOST_LOG_TRIVIAL ( info ) < < " process_task " < < t . to_string ( ) ;
try {
boost : : system : : error_code ec ;
boost : : filesystem : : remove ( t . path + " /.3mf " , ec ) ;
// We Saved with SplitModel now, so we can safe delete these sub models.
boost : : filesystem : : remove_all ( t . path + " /3D/Objects " ) ;
boost : : filesystem : : create_directory ( t . path + " /3D/Objects " ) ;
}
catch ( . . . ) { }
}
}
}
public :
void operator ( ) ( ) {
boost : : unique_lock lock ( m_mutex ) ;
while ( true )
{
while ( m_tasks . empty ( ) ) {
if ( m_interval > 0 )
m_cond . timed_wait ( lock , m_next_backup ) ;
else
m_cond . wait ( lock ) ;
if ( m_interval > 0 & & boost : : get_system_time ( ) > m_next_backup ) {
m_tasks . push_back ( { Backup , 0 , std : : string ( ) , nullptr , + + m_task_seq } ) ;
m_next_backup + = boost : : posix_time : : seconds ( m_interval ) ;
// Maybe wakeup from power sleep
if ( m_next_backup < boost : : get_system_time ( ) )
m_next_backup = boost : : get_system_time ( ) + boost : : posix_time : : seconds ( m_interval ) ;
}
}
Task t = m_tasks . front ( ) ;
if ( t . type = = Exit ) break ;
if ( t . object & & t . delay ) {
if ( ! delay_task ( t , lock ) )
continue ;
}
m_tasks . pop_front ( ) ;
auto callback = m_post_callback ;
lock . unlock ( ) ;
process_task ( t ) ;
lock . lock ( ) ;
if ( t . type > None ) {
m_ui_tasks . push_back ( t ) ;
if ( m_ui_tasks . size ( ) = = 1 & & callback )
callback ( 0 ) ;
}
}
}
bool delay_task ( Task & t , boost : : unique_lock < boost : : mutex > & lock ) {
// delay last task for 3 seconds after last modify
auto now = boost : : get_system_time ( ) ;
auto delay_expire = now + boost : : posix_time : : seconds ( 10 ) ; // must not delay over this time
auto wait = now + boost : : posix_time : : seconds ( 3 ) ;
while ( true ) {
m_cond . timed_wait ( lock , wait ) ;
// Only delay when it's the only-one task
if ( m_tasks . size ( ) ! = 1 | | m_tasks . front ( ) . delay = = t . delay )
break ;
t . delay = m_tasks . front ( ) . delay ;
now = boost : : get_system_time ( ) ;
if ( now > = delay_expire )
break ;
wait = now + boost : : posix_time : : seconds ( 3 ) ;
if ( wait > delay_expire )
wait = delay_expire ;
} ;
// task maybe canceled
if ( m_tasks . empty ( ) )
return false ;
t = m_tasks . front ( ) ;
return true ;
}
private :
boost : : mutex m_mutex ;
boost : : condition_variable m_cond ;
std : : deque < Task > m_tasks ;
std : : deque < Task > m_ui_tasks ;
size_t m_task_seq = 0 ;
// param 0: should call run_ui_tasks
// param 1: should backup current project
std : : function < void ( int ) > m_post_callback ;
long m_interval = 1 * 60 ;
boost : : system_time m_next_backup ;
Model m_temp_model ; // visit only in main thread
bool m_other_changes = false ; // visit only in main thread
bool m_other_changes_backup = false ; // visit only in main thread
std : : vector < std : : pair < ModelObject * , size_t > > m_gaurd_objects ;
boost : : thread m_thread ;
} ;
//QDS: add plate data list related logic
bool load_qds_3mf ( const char * path , DynamicPrintConfig * config , ConfigSubstitutionContext * config_substitutions , Model * model , PlateDataPtrs * plate_data_list , std : : vector < Preset * > * project_presets ,
bool * is_qdt_3mf , Semver * file_version , Import3mfProgressFn proFn , LoadStrategy strategy , QDTProject * project , int plate_id )
{
if ( path = = nullptr | | config = = nullptr | | model = = nullptr )
return false ;
// All import should use "C" locales for number formatting.
CNumericLocalesSetter locales_setter ;
_QDS_3MF_Importer importer ;
bool res = importer . load_model_from_file ( path , * model , * plate_data_list , * project_presets , * config , * config_substitutions , strategy , * is_qdt_3mf , * file_version , proFn , project , plate_id ) ;
importer . log_errors ( ) ;
//QDS: remove legacy project logic currently
//handle_legacy_project_loaded(importer.version(), *config);
return res ;
}
std : : string qds_3mf_get_thumbnail ( const char * path )
{
_QDS_3MF_Importer importer ;
std : : string data ;
bool res = importer . get_thumbnail ( path , data ) ;
if ( ! res ) importer . log_errors ( ) ;
return data ;
}
bool load_gcode_3mf_from_stream ( std : : istream & data , DynamicPrintConfig * config , Model * model , PlateDataPtrs * plate_data_list , Semver * file_version )
{
CNumericLocalesSetter locales_setter ;
_QDS_3MF_Importer importer ;
bool res = importer . load_gcode_3mf_from_stream ( data , * model , * plate_data_list , * config , * file_version ) ;
importer . log_errors ( ) ;
return res ;
}
bool store_qds_3mf ( StoreParams & store_params )
{
// All export should use "C" locales for number formatting.
CNumericLocalesSetter locales_setter ;
if ( store_params . path = = nullptr | | store_params . model = = nullptr )
return false ;
_QDS_3MF_Exporter exporter ;
bool res = exporter . save_model_to_file ( store_params ) ;
if ( ! res )
exporter . log_errors ( ) ;
return res ;
}
//QDS: release plate data list
void release_PlateData_list ( PlateDataPtrs & plate_data_list )
{
//clear
for ( unsigned int i = 0 ; i < plate_data_list . size ( ) ; i + + )
{
delete plate_data_list [ i ] ;
}
plate_data_list . clear ( ) ;
return ;
}
// backup interface
void save_object_mesh ( ModelObject & object )
{
if ( ! object . get_model ( ) | | ! object . get_model ( ) - > is_need_backup ( ) )
return ;
if ( object . volumes . empty ( ) | | object . instances . empty ( ) )
return ;
_QDS_Backup_Manager : : get ( ) . add_object_mesh ( object ) ;
}
void delete_object_mesh ( ModelObject & object )
{
// not really remove
// _QDS_Backup_Manager::get().remove_object_mesh(object);
}
void backup_soon ( )
{
_QDS_Backup_Manager : : get ( ) . backup_soon ( ) ;
}
void remove_backup ( Model & model , bool removeAll )
{
_QDS_Backup_Manager : : get ( ) . remove_backup ( model , removeAll ) ;
}
void set_backup_interval ( long interval )
{
_QDS_Backup_Manager : : get ( ) . set_interval ( interval ) ;
}
void set_backup_callback ( std : : function < void ( int ) > callback )
{
_QDS_Backup_Manager : : get ( ) . set_post_callback ( callback ) ;
}
void run_backup_ui_tasks ( )
{
_QDS_Backup_Manager : : get ( ) . run_ui_tasks ( ) ;
}
bool has_restore_data ( std : : string & path , std : : string & origin )
{
if ( path . empty ( ) ) {
origin = " <lock> " ;
return false ;
}
if ( boost : : filesystem : : exists ( path + " /lock.txt " ) ) {
std : : string pid ;
boost : : filesystem : : load_string_file ( path + " /lock.txt " , pid ) ;
try {
if ( get_process_name ( boost : : lexical_cast < int > ( pid ) ) = =
get_process_name ( 0 ) ) {
origin = " <lock> " ;
return false ;
}
}
catch ( . . . ) {
return false ;
}
}
std : : string file3mf = path + " /.3mf " ;
if ( ! boost : : filesystem : : exists ( file3mf ) )
return false ;
try {
if ( boost : : filesystem : : exists ( path + " /origin.txt " ) )
boost : : filesystem : : load_string_file ( path + " /origin.txt " , origin ) ;
}
catch ( . . . ) {
}
path = file3mf ;
return true ;
}
void put_other_changes ( )
{
_QDS_Backup_Manager : : get ( ) . put_other_changes ( ) ;
}
void clear_other_changes ( bool backup )
{
_QDS_Backup_Manager : : get ( ) . clear_other_changes ( backup ) ;
}
bool has_other_changes ( bool backup )
{
return _QDS_Backup_Manager : : get ( ) . has_other_changes ( backup ) ;
}
SaveObjectGaurd : : SaveObjectGaurd ( ModelObject & object )
{
_QDS_Backup_Manager : : get ( ) . push_object_gaurd ( object ) ;
}
SaveObjectGaurd : : ~ SaveObjectGaurd ( )
{
_QDS_Backup_Manager : : get ( ) . pop_object_gaurd ( ) ;
}
} // namespace Slic3r