Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::_3MF_Exporter Class Reference
+ Inheritance diagram for Slic3r::_3MF_Exporter:
+ Collaboration diagram for Slic3r::_3MF_Exporter:

Classes

struct  BuildItem
 
struct  ObjectData
 
struct  Offsets
 

Public Member Functions

bool save_model_to_file (const std::string &filename, Model &model, const DynamicPrintConfig *config, bool fullpath_sources, const ThumbnailData *thumbnail_data, bool zip64)
 
void log_errors ()
 

Static Public Member Functions

static void add_transformation (std::stringstream &stream, const Transform3d &tr)
 

Protected Member Functions

void add_error (const std::string &error)
 
void clear_errors ()
 

Private Types

typedef std::map< const ModelVolume *, OffsetsVolumeToOffsetsMap
 
typedef std::vector< BuildItemBuildItemsList
 
typedef std::map< int, ObjectDataIdToObjectDataMap
 

Private Member Functions

bool _save_model_to_file (const std::string &filename, Model &model, const DynamicPrintConfig *config, const ThumbnailData *thumbnail_data)
 
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)
 
bool _add_relationships_file_to_archive (mz_zip_archive &archive)
 
bool _add_model_file_to_archive (const std::string &filename, mz_zip_archive &archive, const Model &model, IdToObjectDataMap &objects_data)
 
bool _add_object_to_model_stream (mz_zip_writer_staged_context &context, unsigned int &object_id, ModelObject &object, BuildItemsList &build_items, VolumeToOffsetsMap &volumes_offsets)
 
bool _add_mesh_to_object_stream (mz_zip_writer_staged_context &context, ModelObject &object, VolumeToOffsetsMap &volumes_offsets)
 
bool _add_build_to_model_stream (std::stringstream &stream, const BuildItemsList &build_items)
 
bool _add_cut_information_file_to_archive (mz_zip_archive &archive, Model &model)
 
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)
 
bool _add_model_config_file_to_archive (mz_zip_archive &archive, const Model &model, const IdToObjectDataMap &objects_data)
 
bool _add_custom_gcode_per_print_z_file_to_archive (mz_zip_archive &archive, Model &model, const DynamicPrintConfig *config)
 

Private Attributes

bool m_fullpath_sources { true }
 
bool m_zip64 { true }
 
std::vector< std::string > m_errors
 

Detailed Description

Member Typedef Documentation

◆ BuildItemsList

typedef std::vector<BuildItem> Slic3r::_3MF_Exporter::BuildItemsList
private

◆ IdToObjectDataMap

typedef std::map<int, ObjectData> Slic3r::_3MF_Exporter::IdToObjectDataMap
private

◆ VolumeToOffsetsMap

Member Function Documentation

◆ _add_build_to_model_stream()

bool Slic3r::_3MF_Exporter::_add_build_to_model_stream ( std::stringstream &  stream,
const BuildItemsList build_items 
)
private
2937 {
2938 // This happens for empty projects
2939 if (build_items.size() == 0) {
2940 add_error("No build item found");
2941 return true;
2942 }
2943
2944 stream << " <" << BUILD_TAG << ">\n";
2945
2946 for (const BuildItem& item : build_items) {
2947 stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id << "\" " << TRANSFORM_ATTR << "=\"";
2948 add_transformation(stream, item.transform);
2949 stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
2950 }
2951
2952 stream << " </" << BUILD_TAG << ">\n";
2953
2954 return true;
2955 }
static constexpr const char * BUILD_TAG
Definition 3mf.cpp:95
static constexpr const char * ITEM_TAG
Definition 3mf.cpp:96
static constexpr const char * PRINTABLE_ATTR
Definition 3mf.cpp:114
static constexpr const char * TRANSFORM_ATTR
Definition 3mf.cpp:113
static constexpr const char * OBJECTID_ATTR
Definition 3mf.cpp:112
void add_error(const std::string &error)
Definition 3mf.cpp:305
static void add_transformation(std::stringstream &stream, const Transform3d &tr)
Definition 3mf.cpp:2926

References BUILD_TAG, ITEM_TAG, OBJECTID_ATTR, PRINTABLE_ATTR, and TRANSFORM_ATTR.

◆ _add_content_types_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_content_types_file_to_archive ( mz_zip_archive archive)
private
2529 {
2530 std::stringstream stream;
2531 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2532 stream << "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n";
2533 stream << " <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n";
2534 stream << " <Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\"/>\n";
2535 stream << " <Default Extension=\"png\" ContentType=\"image/png\"/>\n";
2536 stream << "</Types>";
2537
2538 std::string out = stream.str();
2539
2540 if (!mz_zip_writer_add_mem(&archive, CONTENT_TYPES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
2541 add_error("Unable to add content types file to archive");
2542 return false;
2543 }
2544
2545 return true;
2546 }
const std::string CONTENT_TYPES_FILE
Definition 3mf.cpp:73
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
Definition miniz.c:5902
@ MZ_DEFAULT_COMPRESSION
Definition miniz.h:238

References CONTENT_TYPES_FILE, MZ_DEFAULT_COMPRESSION, and mz_zip_writer_add_mem().

+ Here is the call graph for this function:

◆ _add_custom_gcode_per_print_z_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive ( mz_zip_archive archive,
Model model,
const DynamicPrintConfig config 
)
private
3351{
3352 std::string out = "";
3353
3354 if (!model.custom_gcode_per_print_z.gcodes.empty()) {
3355 pt::ptree tree;
3356 pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
3357
3358 for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
3359 pt::ptree& code_tree = main_tree.add("code", "");
3360
3361 // store data of custom_gcode_per_print_z
3362 code_tree.put("<xmlattr>.print_z" , code.print_z );
3363 code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
3364 code_tree.put("<xmlattr>.extruder" , code.extruder );
3365 code_tree.put("<xmlattr>.color" , code.color );
3366 code_tree.put("<xmlattr>.extra" , code.extra );
3367
3368 // add gcode field data for the old version of the PrusaSlicer
3369 std::string gcode = code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
3370 code.type == CustomGCode::PausePrint ? config->opt_string("pause_print_gcode") :
3371 code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
3372 code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
3373 code_tree.put("<xmlattr>.gcode" , gcode );
3374 }
3375
3376 pt::ptree& mode_tree = main_tree.add("mode", "");
3377 // store mode of a custom_gcode_per_print_z
3378 mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
3379 model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
3380 CustomGCode::MultiExtruderMode);
3381
3382 if (!tree.empty()) {
3383 std::ostringstream oss;
3384 boost::property_tree::write_xml(oss, tree);
3385 out = oss.str();
3386
3387 // Post processing("beautification") of the output string
3388 boost::replace_all(out, "><", ">\n<");
3389 }
3390 }
3391
3392 if (!out.empty()) {
3393 if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3394 add_error("Unable to add custom Gcodes per print_z file to archive");
3395 return false;
3396 }
3397 }
3398
3399 return true;
3400}
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE
Definition 3mf.cpp:82
static constexpr char SingleExtruderMode[]
Definition CustomGCode.hpp:58
static constexpr char MultiAsSingleMode[]
Definition CustomGCode.hpp:59
Mode
Definition CustomGCode.hpp:48
@ MultiAsSingle
Definition CustomGCode.hpp:51
@ SingleExtruder
Definition CustomGCode.hpp:50
static constexpr char MultiExtruderMode[]
Definition CustomGCode.hpp:60
@ PausePrint
Definition CustomGCode.hpp:16
@ ToolChange
Definition CustomGCode.hpp:17
@ Template
Definition CustomGCode.hpp:18
@ ColorChange
Definition CustomGCode.hpp:15
IGL_INLINE void mode(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &X, const int d, Eigen::Matrix< T, Eigen::Dynamic, 1 > &M)
Definition mode.cpp:14

References Slic3r::Model::custom_gcode_per_print_z, CUSTOM_GCODE_PER_PRINT_Z_FILE, Slic3r::CustomGCode::Info::gcodes, Slic3r::CustomGCode::Info::mode, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), and Slic3r::ConfigBase::opt_string().

+ Here is the call graph for this function:

◆ _add_cut_information_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_cut_information_file_to_archive ( mz_zip_archive archive,
Model model 
)
private
2958 {
2959 std::string out = "";
2960 pt::ptree tree;
2961
2962 unsigned int object_cnt = 0;
2963 for (const ModelObject* object : model.objects) {
2964 object_cnt++;
2965 if (!object->is_cut())
2966 continue;
2967 pt::ptree& obj_tree = tree.add("objects.object", "");
2968
2969 obj_tree.put("<xmlattr>.id", object_cnt);
2970
2971 // Store info for cut_id
2972 pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
2973
2974 // store cut_id atributes
2975 cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
2976 cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
2977 cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
2978
2979 int volume_idx = -1;
2980 for (const ModelVolume* volume : object->volumes) {
2981 ++volume_idx;
2982 if (volume->is_cut_connector()) {
2983 pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
2984 connectors_tree.put("<xmlattr>.volume_id", volume_idx);
2985 connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
2986 connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
2987 connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
2988 }
2989 }
2990 }
2991
2992 if (!tree.empty()) {
2993 std::ostringstream oss;
2994 pt::write_xml(oss, tree);
2995 out = oss.str();
2996
2997 // Post processing("beautification") of the output string for a better preview
2998 boost::replace_all(out, "><object", ">\n <object");
2999 boost::replace_all(out, "><cut_id", ">\n <cut_id");
3000 boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
3001 boost::replace_all(out, "><connectors", ">\n <connectors");
3002 boost::replace_all(out, "></connectors>", ">\n </connectors>");
3003 boost::replace_all(out, "><connector", ">\n <connector");
3004 boost::replace_all(out, "></connector>", ">\n </connector>");
3005 boost::replace_all(out, "></object>", ">\n </object>");
3006 // OR just
3007 boost::replace_all(out, "><", ">\n<");
3008 }
3009
3010 if (!out.empty()) {
3011 if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3012 add_error("Unable to add cut information file to archive");
3013 return false;
3014 }
3015 }
3016
3017 return true;
3018 }
const std::string CUT_INFORMATION_FILE
Definition 3mf.cpp:83
IGL_INLINE void volume(const Eigen::MatrixBase< DerivedV > &V, const Eigen::MatrixBase< DerivedT > &T, Eigen::PlainObjectBase< Derivedvol > &vol)
Definition volume.cpp:15

References CUT_INFORMATION_FILE, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), and Slic3r::Model::objects.

+ Here is the call graph for this function:

◆ _add_layer_config_ranges_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_layer_config_ranges_file_to_archive ( mz_zip_archive archive,
Model model 
)
private
3055 {
3056 std::string out = "";
3057 pt::ptree tree;
3058
3059 unsigned int object_cnt = 0;
3060 for (const ModelObject* object : model.objects) {
3061 object_cnt++;
3062 const t_layer_config_ranges& ranges = object->layer_config_ranges;
3063 if (!ranges.empty())
3064 {
3065 pt::ptree& obj_tree = tree.add("objects.object","");
3066
3067 obj_tree.put("<xmlattr>.id", object_cnt);
3068
3069 // Store the layer config ranges.
3070 for (const auto& range : ranges) {
3071 pt::ptree& range_tree = obj_tree.add("range", "");
3072
3073 // store minX and maxZ
3074 range_tree.put("<xmlattr>.min_z", range.first.first);
3075 range_tree.put("<xmlattr>.max_z", range.first.second);
3076
3077 // store range configuration
3078 const ModelConfig& config = range.second;
3079 for (const std::string& opt_key : config.keys()) {
3080 pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
3081 opt_tree.put("<xmlattr>.opt_key", opt_key);
3082 }
3083 }
3084 }
3085 }
3086
3087 if (!tree.empty()) {
3088 std::ostringstream oss;
3089 pt::write_xml(oss, tree);
3090 out = oss.str();
3091
3092 // Post processing("beautification") of the output string for a better preview
3093 boost::replace_all(out, "><object", ">\n <object");
3094 boost::replace_all(out, "><range", ">\n <range");
3095 boost::replace_all(out, "><option", ">\n <option");
3096 boost::replace_all(out, "></range>", ">\n </range>");
3097 boost::replace_all(out, "></object>", ">\n </object>");
3098 // OR just
3099 boost::replace_all(out, "><", ">\n<");
3100 }
3101
3102 if (!out.empty()) {
3103 if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3104 add_error("Unable to add layer heights profile file to archive");
3105 return false;
3106 }
3107 }
3108
3109 return true;
3110 }
const std::string LAYER_CONFIG_RANGES_FILE
Definition 3mf.cpp:79
auto range(Cont &&cont)
Definition libslic3r.h:356
std::map< t_layer_height_range, ModelConfig > t_layer_config_ranges
Definition Slicing.hpp:130

References Slic3r::ModelConfig::keys(), LAYER_CONFIG_RANGES_FILE, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), Slic3r::Model::objects, Slic3r::ModelConfig::opt_serialize(), and Slic3r::range().

+ Here is the call graph for this function:

◆ _add_layer_height_profile_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_layer_height_profile_file_to_archive ( mz_zip_archive archive,
Model model 
)
private
3021 {
3023 std::string out = "";
3024 char buffer[1024];
3025
3026 unsigned int count = 0;
3027 for (const ModelObject* object : model.objects) {
3028 ++count;
3029 const std::vector<double>& layer_height_profile = object->layer_height_profile.get();
3030 if (layer_height_profile.size() >= 4 && layer_height_profile.size() % 2 == 0) {
3031 sprintf(buffer, "object_id=%d|", count);
3032 out += buffer;
3033
3034 // Store the layer height profile as a single semicolon separated list.
3035 for (size_t i = 0; i < layer_height_profile.size(); ++i) {
3036 sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
3037 out += buffer;
3038 }
3039
3040 out += "\n";
3041 }
3042 }
3043
3044 if (!out.empty()) {
3045 if (!mz_zip_writer_add_mem(&archive, LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3046 add_error("Unable to add layer heights profile file to archive");
3047 return false;
3048 }
3049 }
3050
3051 return true;
3052 }
const std::string LAYER_HEIGHTS_PROFILE_FILE
Definition 3mf.cpp:78
bool is_decimal_separator_point()
Definition LocalesUtils.cpp:47
IGL_INLINE void count(const Eigen::SparseMatrix< XType > &X, const int dim, Eigen::SparseVector< SType > &S)
Definition count.cpp:12

References Slic3r::is_decimal_separator_point(), LAYER_HEIGHTS_PROFILE_FILE, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), and Slic3r::Model::objects.

+ Here is the call graph for this function:

◆ _add_mesh_to_object_stream()

bool Slic3r::_3MF_Exporter::_add_mesh_to_object_stream ( mz_zip_writer_staged_context context,
ModelObject object,
VolumeToOffsetsMap volumes_offsets 
)
private
2762 {
2763 std::string output_buffer;
2764 output_buffer += " <";
2765 output_buffer += MESH_TAG;
2766 output_buffer += ">\n <";
2767 output_buffer += VERTICES_TAG;
2768 output_buffer += ">\n";
2769
2770 auto flush = [this, &output_buffer, &context](bool force = false) {
2771 if ((force && ! output_buffer.empty()) || output_buffer.size() >= 65536 * 16) {
2772 if (! mz_zip_writer_add_staged_data(&context, output_buffer.data(), output_buffer.size())) {
2773 add_error("Error during writing or compression");
2774 return false;
2775 }
2776 output_buffer.clear();
2777 }
2778 return true;
2779 };
2780
2781 auto format_coordinate = [](float f, char *buf) -> char* {
2783#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
2784 // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
2785 // https://github.com/boostorg/spirit/pull/586
2786 // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
2787 // The code is left here for the ocasion boost guys improve.
2788 coordinate_type_fixed const coordinate_fixed = coordinate_type_fixed();
2789 coordinate_type_scientific const coordinate_scientific = coordinate_type_scientific();
2790 // Format "f" in a fixed format.
2791 char *ptr = buf;
2792 boost::spirit::karma::generate(ptr, coordinate_fixed, f);
2793 // Format "f" in a scientific format.
2794 char *ptr2 = ptr;
2795 boost::spirit::karma::generate(ptr2, coordinate_scientific, f);
2796 // Return end of the shorter string.
2797 auto len2 = ptr2 - ptr;
2798 if (ptr - buf > len2) {
2799 // Move the shorter scientific form to the front.
2800 memcpy(buf, ptr, len2);
2801 ptr = buf + len2;
2802 }
2803 // Return pointer to the end.
2804 return ptr;
2805#else
2806 // Round-trippable float, shortest possible.
2807 return buf + sprintf(buf, "%.9g", f);
2808#endif
2809 };
2810
2811 char buf[256];
2812 unsigned int vertices_count = 0;
2813 for (ModelVolume* volume : object.volumes) {
2814 if (volume == nullptr)
2815 continue;
2816
2817 volumes_offsets.insert({ volume, Offsets(vertices_count) });
2818
2819 const indexed_triangle_set &its = volume->mesh().its;
2820 if (its.vertices.empty()) {
2821 add_error("Found invalid mesh");
2822 return false;
2823 }
2824
2825 vertices_count += (int)its.vertices.size();
2826
2827 const Transform3d& matrix = volume->get_matrix();
2828 for (const auto& vertex: its.vertices) {
2829 Vec3f v = (matrix * vertex.cast<double>()).cast<float>();
2830 char *ptr = buf;
2831 boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\"");
2832 ptr = format_coordinate(v.x(), ptr);
2833 boost::spirit::karma::generate(ptr, "\" y=\"");
2834 ptr = format_coordinate(v.y(), ptr);
2835 boost::spirit::karma::generate(ptr, "\" z=\"");
2836 ptr = format_coordinate(v.z(), ptr);
2837 boost::spirit::karma::generate(ptr, "\"/>\n");
2838 *ptr = '\0';
2839 output_buffer += buf;
2840 if (! flush())
2841 return false;
2842 }
2843 }
2844
2845 output_buffer += " </";
2846 output_buffer += VERTICES_TAG;
2847 output_buffer += ">\n <";
2848 output_buffer += TRIANGLES_TAG;
2849 output_buffer += ">\n";
2850
2851 unsigned int triangles_count = 0;
2852 for (ModelVolume* volume : object.volumes) {
2853 if (volume == nullptr)
2854 continue;
2855
2856 bool is_left_handed = volume->is_left_handed();
2857 VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
2858 assert(volume_it != volumes_offsets.end());
2859
2860 const indexed_triangle_set &its = volume->mesh().its;
2861
2862 // updates triangle offsets
2863 volume_it->second.first_triangle_id = triangles_count;
2864 triangles_count += (int)its.indices.size();
2865 volume_it->second.last_triangle_id = triangles_count - 1;
2866
2867 for (int i = 0; i < int(its.indices.size()); ++ i) {
2868 {
2869 const Vec3i &idx = its.indices[i];
2870 char *ptr = buf;
2871 boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
2872 " v1=\"" << boost::spirit::int_ <<
2873 "\" v2=\"" << boost::spirit::int_ <<
2874 "\" v3=\"" << boost::spirit::int_ << "\"",
2875 idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
2876 idx[1] + volume_it->second.first_vertex_id,
2877 idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
2878 *ptr = '\0';
2879 output_buffer += buf;
2880 }
2881
2882 std::string custom_supports_data_string = volume->supported_facets.get_triangle_as_string(i);
2883 if (! custom_supports_data_string.empty()) {
2884 output_buffer += " ";
2885 output_buffer += CUSTOM_SUPPORTS_ATTR;
2886 output_buffer += "=\"";
2887 output_buffer += custom_supports_data_string;
2888 output_buffer += "\"";
2889 }
2890
2891 std::string custom_seam_data_string = volume->seam_facets.get_triangle_as_string(i);
2892 if (! custom_seam_data_string.empty()) {
2893 output_buffer += " ";
2894 output_buffer += CUSTOM_SEAM_ATTR;
2895 output_buffer += "=\"";
2896 output_buffer += custom_seam_data_string;
2897 output_buffer += "\"";
2898 }
2899
2900 std::string mmu_painting_data_string = volume->mmu_segmentation_facets.get_triangle_as_string(i);
2901 if (! mmu_painting_data_string.empty()) {
2902 output_buffer += " ";
2903 output_buffer += MMU_SEGMENTATION_ATTR;
2904 output_buffer += "=\"";
2905 output_buffer += mmu_painting_data_string;
2906 output_buffer += "\"";
2907 }
2908
2909 output_buffer += "/>\n";
2910
2911 if (! flush())
2912 return false;
2913 }
2914 }
2915
2916 output_buffer += " </";
2917 output_buffer += TRIANGLES_TAG;
2918 output_buffer += ">\n </";
2919 output_buffer += MESH_TAG;
2920 output_buffer += ">\n";
2921
2922 // Force flush.
2923 return flush(true);
2924 }
static constexpr const char * MMU_SEGMENTATION_ATTR
Definition 3mf.cpp:118
static constexpr const char * TRIANGLES_TAG
Definition 3mf.cpp:91
static constexpr const char * CUSTOM_SEAM_ATTR
Definition 3mf.cpp:117
static constexpr const char * CUSTOM_SUPPORTS_ATTR
Definition 3mf.cpp:116
static constexpr const char * VERTEX_TAG
Definition 3mf.cpp:90
static constexpr const char * MESH_TAG
Definition 3mf.cpp:88
static constexpr const char * VERTICES_TAG
Definition 3mf.cpp:89
static constexpr const char * TRIANGLE_TAG
Definition 3mf.cpp:92
mz_bool mz_zip_writer_add_staged_data(mz_zip_writer_staged_context *pContext, const char *pRead_buf, size_t n)
Definition miniz.c:6856
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:81
Eigen::Matrix< int, 3, 1, Eigen::DontAlign > Vec3i
Definition Point.hpp:40
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:49
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:12
TPoint< S > & vertex(S &sh, unsigned long idx, const PolygonTag &)
Definition geometry_traits.hpp:1180
Definition stl.h:157
std::vector< stl_vertex > vertices
Definition stl.h:165
std::vector< stl_triangle_vertex_indices > indices
Definition stl.h:164

References CUSTOM_SEAM_ATTR, CUSTOM_SUPPORTS_ATTR, indexed_triangle_set::indices, Slic3r::is_decimal_separator_point(), MESH_TAG, MMU_SEGMENTATION_ATTR, mz_zip_writer_add_staged_data(), TRIANGLE_TAG, TRIANGLES_TAG, VERTEX_TAG, indexed_triangle_set::vertices, and VERTICES_TAG.

+ Here is the call graph for this function:

◆ _add_model_config_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_model_config_file_to_archive ( mz_zip_archive archive,
const Model model,
const IdToObjectDataMap objects_data 
)
private
3220 {
3221 enum class MetadataType{
3222 object,
3223 volume
3224 };
3225
3226 auto add_metadata = [](std::stringstream &stream, unsigned indent, MetadataType type,
3227 const std::string &key, const std::string &value) {
3228 const char *type_value;
3229 switch (type) {
3230 case MetadataType::object: type_value = OBJECT_TYPE; break;
3231 case MetadataType::volume: type_value = VOLUME_TYPE; break;
3232 };
3233 stream << std::string(indent, ' ') << '<' << METADATA_TAG << " "
3234 << TYPE_ATTR << "=\"" << type_value << "\" "
3235 << KEY_ATTR << "=\"" << key << "\" "
3236 << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(value) << "\"/>\n";
3237 };
3238
3239 std::stringstream stream;
3240 // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
3241 // when loaded as accurately as possible.
3242 stream << std::setprecision(std::numeric_limits<double>::max_digits10);
3243 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
3244 stream << "<" << CONFIG_TAG << ">\n";
3245
3246 for (const IdToObjectDataMap::value_type& obj_metadata : objects_data) {
3247 const ModelObject* obj = obj_metadata.second.object;
3248 if (obj == nullptr) continue;
3249 // Output of instances count added because of github #3435, currently not used by PrusaSlicer
3250 stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
3251
3252 // stores object's name
3253 if (!obj->name.empty())
3254 add_metadata(stream, 2, MetadataType::object, "name", obj->name);
3255 // stores object's config data
3256 const ModelConfigObject &config = obj->config;
3257 for (const std::string& key : config.keys())
3258 add_metadata(stream, 2, MetadataType::object, key, config.opt_serialize(key));
3259
3260 for (const ModelVolume* volume : obj_metadata.second.object->volumes) {
3261 if (volume == nullptr) continue;
3262 const VolumeToOffsetsMap& offsets = obj_metadata.second.volumes_offsets;
3263 VolumeToOffsetsMap::const_iterator it = offsets.find(volume);
3264 if (it != offsets.end()) {
3265 // stores volume's offsets
3266 stream << " <" << VOLUME_TAG << " ";
3267 stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
3268 stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\">\n";
3269
3270 // stores volume's name
3271 if (!volume->name.empty())
3272 add_metadata(stream, 3, MetadataType::volume, NAME_KEY, volume->name);
3273
3274 // stores volume's modifier field (legacy, to support old slicers)
3275 if (volume->is_modifier())
3276 add_metadata(stream, 3, MetadataType::volume, MODIFIER_KEY, "1");
3277 // stores volume's type (overrides the modifier field above)
3278 add_metadata(stream, 3, MetadataType::volume, VOLUME_TYPE_KEY, ModelVolume::type_to_string(volume->type()));
3279
3280 // stores volume's local matrix
3281 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
3282 const Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
3283 for (int r = 0; r < 4; ++r) {
3284 for (int c = 0; c < 4; ++c) {
3285 stream << matrix(r, c);
3286 if (r != 3 || c != 3)
3287 stream << " ";
3288 }
3289 }
3290 stream << "\"/>\n";
3291
3292 // stores volume's source data
3293 {
3294 std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
3295 std::string prefix = std::string(" <") + METADATA_TAG + " " + TYPE_ATTR + "=\"" + VOLUME_TYPE + "\" " + KEY_ATTR + "=\"";
3296 if (! volume->source.input_file.empty()) {
3297 stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
3298 stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
3299 stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
3300 stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
3301 stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
3302 stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
3303 }
3304 assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
3305 if (volume->source.is_converted_from_inches)
3306 stream << prefix << SOURCE_IN_INCHES_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
3307 else if (volume->source.is_converted_from_meters)
3308 stream << prefix << SOURCE_IN_METERS_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
3309 if (volume->source.is_from_builtin_objects)
3310 stream << prefix << SOURCE_IS_BUILTIN_VOLUME_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
3311 }
3312
3313 // stores volume's config data
3314 for (const std::string& key : volume->config.keys()) {
3315 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
3316 }
3317
3318 // stores volume's text data
3319 const auto &tc = volume->text_configuration;
3320 if (tc.has_value())
3322
3323 // stores mesh's statistics
3324 const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
3325 stream << " <" << MESH_TAG << " ";
3326 stream << MESH_STAT_EDGES_FIXED << "=\"" << stats.edges_fixed << "\" ";
3327 stream << MESH_STAT_DEGENERATED_FACETS << "=\"" << stats.degenerate_facets << "\" ";
3328 stream << MESH_STAT_FACETS_REMOVED << "=\"" << stats.facets_removed << "\" ";
3329 stream << MESH_STAT_FACETS_RESERVED << "=\"" << stats.facets_reversed << "\" ";
3330 stream << MESH_STAT_BACKWARDS_EDGES << "=\"" << stats.backwards_edges << "\"/>\n";
3331
3332 stream << " </" << VOLUME_TAG << ">\n";
3333 }
3334 }
3335 stream << " </" << OBJECT_TAG << ">\n";
3336 }
3337
3338 stream << "</" << CONFIG_TAG << ">\n";
3339
3340 std::string out = stream.str();
3341
3342 if (!mz_zip_writer_add_mem(&archive, MODEL_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3343 add_error("Unable to add model config file to archive");
3344 return false;
3345 }
3346
3347 return true;
3348 }
static constexpr const char * VOLUME_TYPE
Definition 3mf.cpp:126
static constexpr const char * SOURCE_VOLUME_ID_KEY
Definition 3mf.cpp:134
static constexpr const char * MESH_STAT_BACKWARDS_EDGES
Definition 3mf.cpp:146
static constexpr const char * SOURCE_IN_INCHES_KEY
Definition 3mf.cpp:138
static constexpr const char * NAME_KEY
Definition 3mf.cpp:128
static constexpr const char * SOURCE_IS_BUILTIN_VOLUME_KEY
Definition 3mf.cpp:140
static constexpr const char * SOURCE_IN_METERS_KEY
Definition 3mf.cpp:139
static constexpr const char * OBJECT_TYPE
Definition 3mf.cpp:125
static constexpr const char * FIRST_TRIANGLE_ID_ATTR
Definition 3mf.cpp:122
static constexpr const char * VOLUME_TAG
Definition 3mf.cpp:100
static constexpr const char * MODIFIER_KEY
Definition 3mf.cpp:129
static constexpr const char * MESH_STAT_EDGES_FIXED
Definition 3mf.cpp:142
static constexpr const char * SOURCE_OFFSET_Z_KEY
Definition 3mf.cpp:137
static constexpr const char * SOURCE_OFFSET_X_KEY
Definition 3mf.cpp:135
static constexpr const char * LAST_TRIANGLE_ID_ATTR
Definition 3mf.cpp:123
static constexpr const char * SOURCE_OBJECT_ID_KEY
Definition 3mf.cpp:133
static constexpr const char * MESH_STAT_FACETS_RESERVED
Definition 3mf.cpp:145
static constexpr const char * CONFIG_TAG
Definition 3mf.cpp:99
static constexpr const char * INSTANCESCOUNT_ATTR
Definition 3mf.cpp:115
static constexpr const char * MESH_STAT_FACETS_REMOVED
Definition 3mf.cpp:144
static constexpr const char * VALUE_ATTR
Definition 3mf.cpp:121
static constexpr const char * TYPE_ATTR
Definition 3mf.cpp:104
static constexpr const char * SOURCE_OFFSET_Y_KEY
Definition 3mf.cpp:136
static constexpr const char * MATRIX_KEY
Definition 3mf.cpp:131
const std::string MODEL_CONFIG_FILE
Definition 3mf.cpp:77
static constexpr const char * SOURCE_FILE_KEY
Definition 3mf.cpp:132
static constexpr const char * ID_ATTR
Definition 3mf.cpp:105
static constexpr const char * VOLUME_TYPE_KEY
Definition 3mf.cpp:130
static constexpr const char * KEY_ATTR
Definition 3mf.cpp:120
static constexpr const char * METADATA_TAG
Definition 3mf.cpp:97
static constexpr const char * OBJECT_TAG
Definition 3mf.cpp:87
static constexpr const char * MESH_STAT_DEGENERATED_FACETS
Definition 3mf.cpp:143
std::map< const ModelVolume *, Offsets > VolumeToOffsetsMap
Definition 3mf.cpp:2351
bool m_fullpath_sources
Definition 3mf.cpp:2367
static std::string type_to_string(const ModelVolumeType t)
Definition Model.cpp:2138
TOKEN * string(char *text)
Definition config.c:233
std::string xml_escape(std::string text, bool is_marked)
Definition utils.cpp:934
std::string xml_escape_double_quotes_attribute_value(std::string text)
Definition utils.cpp:964
Definition args.hpp:18
static void create_fix_and_store(std::stringstream &stream, TextConfiguration tc, const ModelVolume &volume)
Definition 3mf.cpp:3554

References Slic3r::RepairedMeshErrors::backwards_edges, Slic3r::ModelObject::config, CONFIG_TAG, Slic3r::RepairedMeshErrors::degenerate_facets, Slic3r::RepairedMeshErrors::edges_fixed, Slic3r::RepairedMeshErrors::facets_removed, Slic3r::RepairedMeshErrors::facets_reversed, FIRST_TRIANGLE_ID_ATTR, ID_ATTR, Slic3r::ModelObject::instances, INSTANCESCOUNT_ATTR, KEY_ATTR, Slic3r::ModelConfig::keys(), LAST_TRIANGLE_ID_ATTR, MATRIX_KEY, MESH_STAT_BACKWARDS_EDGES, MESH_STAT_DEGENERATED_FACETS, MESH_STAT_EDGES_FIXED, MESH_STAT_FACETS_REMOVED, MESH_STAT_FACETS_RESERVED, MESH_TAG, METADATA_TAG, MODEL_CONFIG_FILE, MODIFIER_KEY, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), Slic3r::ModelObject::name, NAME_KEY, OBJECT_TAG, OBJECT_TYPE, Slic3r::ModelConfig::opt_serialize(), SOURCE_FILE_KEY, SOURCE_IN_INCHES_KEY, SOURCE_IN_METERS_KEY, SOURCE_IS_BUILTIN_VOLUME_KEY, SOURCE_OBJECT_ID_KEY, SOURCE_OFFSET_X_KEY, SOURCE_OFFSET_Y_KEY, SOURCE_OFFSET_Z_KEY, SOURCE_VOLUME_ID_KEY, TYPE_ATTR, VALUE_ATTR, VOLUME_TAG, VOLUME_TYPE, VOLUME_TYPE_KEY, Slic3r::xml_escape(), and Slic3r::xml_escape_double_quotes_attribute_value().

+ Here is the call graph for this function:

◆ _add_model_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_model_file_to_archive ( const std::string &  filename,
mz_zip_archive archive,
const Model model,
IdToObjectDataMap objects_data 
)
private
2596 {
2598 if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(),
2599 m_zip64 ?
2600 // Maximum expected and allowed 3MF file size is 16GiB.
2601 // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
2602 (uint64_t(1) << 30) * 16 :
2603 // Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
2604 // GH issue #6193.
2605 (uint64_t(1) << 32) - 1,
2606 nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0)) {
2607 add_error("Unable to add model file to archive");
2608 return false;
2609 }
2610
2611 {
2612 std::stringstream stream;
2613 reset_stream(stream);
2614 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2615 stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
2616 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
2617
2618 if (model.is_fdm_support_painted())
2619 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION << "\">" << FDM_SUPPORTS_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
2620
2621 if (model.is_seam_painted())
2622 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_SEAM_PAINTING_VERSION << "\">" << SEAM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
2623
2624 if (model.is_mm_painted())
2625 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_MM_PAINTING_VERSION << "\">" << MM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
2626
2627 std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
2628 stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
2629 stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
2630 stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "</" << METADATA_TAG << ">\n";
2631 stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "</" << METADATA_TAG << ">\n";
2632 stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "</" << METADATA_TAG << ">\n";
2633 stream << " <" << METADATA_TAG << " name=\"Rating\">" << "</" << METADATA_TAG << ">\n";
2635 // keep only the date part of the string
2636 date = date.substr(0, 10);
2637 stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n";
2638 stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n";
2639 stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
2640 stream << " <" << RESOURCES_TAG << ">\n";
2641 std::string buf = stream.str();
2642 if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
2643 add_error("Unable to add model file to archive");
2644 return false;
2645 }
2646 }
2647
2648 // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
2649 BuildItemsList build_items;
2650
2651 // The object_id here is a one based identifier of the first instance of a ModelObject in the 3MF file, where
2652 // all the object instances of all ModelObjects are stored and indexed in a 1 based linear fashion.
2653 // Therefore the list of object_ids here may not be continuous.
2654 unsigned int object_id = 1;
2655 for (ModelObject* obj : model.objects) {
2656 if (obj == nullptr)
2657 continue;
2658
2659 // Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
2660 unsigned int curr_id = object_id;
2661 IdToObjectDataMap::iterator object_it = objects_data.insert({ curr_id, ObjectData(obj) }).first;
2662 // Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
2663 // object_it->second.volumes_offsets will contain the offsets of the ModelVolumes in that single indexed triangle set.
2664 // object_id will be increased to point to the 1st instance of the next ModelObject.
2665 if (!_add_object_to_model_stream(context, object_id, *obj, build_items, object_it->second.volumes_offsets)) {
2666 add_error("Unable to add object to archive");
2668 return false;
2669 }
2670 }
2671
2672 {
2673 std::stringstream stream;
2674 reset_stream(stream);
2675 stream << " </" << RESOURCES_TAG << ">\n";
2676
2677 // Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
2678 if (!_add_build_to_model_stream(stream, build_items)) {
2679 add_error("Unable to add build to archive");
2681 return false;
2682 }
2683
2684 stream << "</" << MODEL_TAG << ">\n";
2685
2686 std::string buf = stream.str();
2687
2688 if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
2689 ! mz_zip_writer_add_staged_finish(&context)) {
2690 add_error("Unable to add model file to archive");
2691 return false;
2692 }
2693 }
2694
2695 return true;
2696 }
const unsigned int SEAM_PAINTING_VERSION
Definition 3mf.cpp:63
const std::string MODEL_FILE
Definition 3mf.cpp:72
const unsigned int VERSION_3MF
Definition 3mf.cpp:54
static constexpr const char * RESOURCES_TAG
Definition 3mf.cpp:86
const std::string SLIC3RPE_SEAM_PAINTING_VERSION
Definition 3mf.cpp:67
const std::string SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION
Definition 3mf.cpp:66
static constexpr const char * MODEL_TAG
Definition 3mf.cpp:85
const unsigned int FDM_SUPPORTS_PAINTING_VERSION
Definition 3mf.cpp:62
const std::string SLIC3RPE_MM_PAINTING_VERSION
Definition 3mf.cpp:68
const unsigned int MM_PAINTING_VERSION
Definition 3mf.cpp:64
const char * SLIC3RPE_3MF_VERSION
Definition 3mf.cpp:57
bool m_zip64
Definition 3mf.cpp:2368
bool _add_build_to_model_stream(std::stringstream &stream, const BuildItemsList &build_items)
Definition 3mf.cpp:2936
std::vector< BuildItem > BuildItemsList
Definition 3mf.cpp:2364
bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int &object_id, ModelObject &object, BuildItemsList &build_items, VolumeToOffsetsMap &volumes_offsets)
Definition 3mf.cpp:2698
mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext)
Definition miniz.c:6886
mz_bool mz_zip_writer_add_staged_open(mz_zip_archive *pZip, mz_zip_writer_staged_context *pContext, const char *pArchive_name, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)
Definition miniz.c:6690
Definition miniz.h:1313
std::string utc_timestamp()
Definition Time.hpp:30
time_t get_current_time_utc()
Definition Time.cpp:175
static void reset_stream(std::stringstream &stream)
Definition 3mf.cpp:2584
unsigned __int64 uint64_t
Definition unistd.h:80

References FDM_SUPPORTS_PAINTING_VERSION, Slic3r::Utils::get_current_time_utc(), Slic3r::Model::is_fdm_support_painted(), Slic3r::Model::is_mm_painted(), Slic3r::Model::is_seam_painted(), METADATA_TAG, MM_PAINTING_VERSION, MODEL_FILE, MODEL_TAG, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_staged_data(), mz_zip_writer_add_staged_finish(), mz_zip_writer_add_staged_open(), Slic3r::Model::objects, Slic3r::reset_stream(), RESOURCES_TAG, SEAM_PAINTING_VERSION, SLIC3RPE_3MF_VERSION, SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION, SLIC3RPE_MM_PAINTING_VERSION, SLIC3RPE_SEAM_PAINTING_VERSION, Slic3r::Utils::utc_timestamp(), VERSION_3MF, and Slic3r::xml_escape().

+ Here is the call graph for this function:

◆ _add_object_to_model_stream()

bool Slic3r::_3MF_Exporter::_add_object_to_model_stream ( mz_zip_writer_staged_context context,
unsigned int &  object_id,
ModelObject object,
BuildItemsList build_items,
VolumeToOffsetsMap volumes_offsets 
)
private
2699 {
2700 std::stringstream stream;
2701 reset_stream(stream);
2702 unsigned int id = 0;
2703 for (const ModelInstance* instance : object.instances) {
2704 assert(instance != nullptr);
2705 if (instance == nullptr)
2706 continue;
2707
2708 unsigned int instance_id = object_id + id;
2709 stream << " <" << OBJECT_TAG << " id=\"" << instance_id << "\" type=\"model\">\n";
2710
2711 if (id == 0) {
2712 std::string buf = stream.str();
2713 reset_stream(stream);
2714 if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
2715 ! _add_mesh_to_object_stream(context, object, volumes_offsets)) {
2716 add_error("Unable to add mesh to archive");
2717 return false;
2718 }
2719 }
2720 else {
2721 stream << " <" << COMPONENTS_TAG << ">\n";
2722 stream << " <" << COMPONENT_TAG << " objectid=\"" << object_id << "\"/>\n";
2723 stream << " </" << COMPONENTS_TAG << ">\n";
2724 }
2725
2726 Transform3d t = instance->get_matrix();
2727 // instance_id is just a 1 indexed index in build_items.
2728 assert(instance_id == build_items.size() + 1);
2729 build_items.emplace_back(instance_id, t, instance->printable);
2730
2731 stream << " </" << OBJECT_TAG << ">\n";
2732
2733 ++id;
2734 }
2735
2736 object_id += id;
2737 std::string buf = stream.str();
2738 return buf.empty() || mz_zip_writer_add_staged_data(&context, buf.data(), buf.size());
2739 }
static constexpr const char * COMPONENTS_TAG
Definition 3mf.cpp:93
static constexpr const char * COMPONENT_TAG
Definition 3mf.cpp:94
bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject &object, VolumeToOffsetsMap &volumes_offsets)
Definition 3mf.cpp:2761

References COMPONENT_TAG, COMPONENTS_TAG, mz_zip_writer_add_staged_data(), OBJECT_TAG, and Slic3r::reset_stream().

+ Here is the call graph for this function:

◆ _add_print_config_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_print_config_file_to_archive ( mz_zip_archive archive,
const DynamicPrintConfig config 
)
private
3199 {
3201 char buffer[1024];
3202 sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
3203 std::string out = buffer;
3204
3205 for (const std::string &key : config.keys())
3206 if (key != "compatible_printers")
3207 out += "; " + key + " = " + config.opt_serialize(key) + "\n";
3208
3209 if (!out.empty()) {
3210 if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3211 add_error("Unable to add print config file to archive");
3212 return false;
3213 }
3214 }
3215
3216 return true;
3217 }
const std::string PRINT_CONFIG_FILE
Definition 3mf.cpp:76
if(!(yy_init))
Definition lexer.c:1190
std::string header_slic3r_generated()
Definition utils.cpp:914

References Slic3r::header_slic3r_generated(), Slic3r::is_decimal_separator_point(), Slic3r::DynamicConfig::keys(), MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), Slic3r::ConfigBase::opt_serialize(), and PRINT_CONFIG_FILE.

+ Here is the call graph for this function:

◆ _add_relationships_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_relationships_file_to_archive ( mz_zip_archive archive)
private
2566 {
2567 std::stringstream stream;
2568 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2569 stream << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n";
2570 stream << " <Relationship Target=\"/" << MODEL_FILE << "\" Id=\"rel-1\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\"/>\n";
2571 stream << " <Relationship Target=\"/" << THUMBNAIL_FILE << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
2572 stream << "</Relationships>";
2573
2574 std::string out = stream.str();
2575
2576 if (!mz_zip_writer_add_mem(&archive, RELATIONSHIPS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
2577 add_error("Unable to add relationships file to archive");
2578 return false;
2579 }
2580
2581 return true;
2582 }
const std::string THUMBNAIL_FILE
Definition 3mf.cpp:75
const std::string RELATIONSHIPS_FILE
Definition 3mf.cpp:74

References MODEL_FILE, MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), RELATIONSHIPS_FILE, and THUMBNAIL_FILE.

+ Here is the call graph for this function:

◆ _add_sla_drain_holes_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_sla_drain_holes_file_to_archive ( mz_zip_archive archive,
Model model 
)
private
3148 {
3150 const char *const fmt = "object_id=%d|";
3151 std::string out;
3152
3153 unsigned int count = 0;
3154 for (const ModelObject* object : model.objects) {
3155 ++count;
3156 sla::DrainHoles drain_holes = object->sla_drain_holes;
3157
3158 // The holes were placed 1mm above the mesh in the first implementation.
3159 // This was a bad idea and the reference point was changed in 2.3 so
3160 // to be on the mesh exactly. The elevated position is still saved
3161 // in 3MFs for compatibility reasons.
3162 for (sla::DrainHole& hole : drain_holes) {
3163 hole.pos -= hole.normal.normalized();
3164 hole.height += 1.f;
3165 }
3166
3167 if (!drain_holes.empty()) {
3168 out += string_printf(fmt, count);
3169
3170 // Store the layer height profile as a single space separated list.
3171 for (size_t i = 0; i < drain_holes.size(); ++i)
3172 out += string_printf((i == 0 ? "%f %f %f %f %f %f %f %f" : " %f %f %f %f %f %f %f %f"),
3173 drain_holes[i].pos(0),
3174 drain_holes[i].pos(1),
3175 drain_holes[i].pos(2),
3176 drain_holes[i].normal(0),
3177 drain_holes[i].normal(1),
3178 drain_holes[i].normal(2),
3179 drain_holes[i].radius,
3180 drain_holes[i].height);
3181
3182 out += "\n";
3183 }
3184 }
3185
3186 if (!out.empty()) {
3187 // Adds version header at the beginning:
3188 out = std::string("drain_holes_format_version=") + std::to_string(drain_holes_format_version) + std::string("\n") + out;
3189
3190 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))) {
3191 add_error("Unable to add sla support points file to archive");
3192 return false;
3193 }
3194 }
3195 return true;
3196 }
const std::string SLA_DRAIN_HOLES_FILE
Definition 3mf.cpp:81
unsigned int mz_uint
Definition miniz.h:489
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:540
Vec< 3, T > normal(const std::array< Vec< 3, T >, 3 > &tri)
Definition Rotfinder.cpp:43
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:14
std::vector< DrainHole > DrainHoles
Definition Hollowing.hpp:76
std::string string_printf(const char *format,...)
Definition utils.cpp:890
@ drain_holes_format_version
Definition 3mf.hpp:24
Slic3r::Polygon & hole(Slic3r::ExPolygon &sh, unsigned long idx)
Definition geometries.hpp:200

References Slic3r::drain_holes_format_version, Slic3r::is_decimal_separator_point(), MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), Slic3r::Model::objects, SLA_DRAIN_HOLES_FILE, and Slic3r::string_printf().

+ Here is the call graph for this function:

◆ _add_sla_support_points_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_sla_support_points_file_to_archive ( mz_zip_archive archive,
Model model 
)
private
3113 {
3115 std::string out = "";
3116 char buffer[1024];
3117
3118 unsigned int count = 0;
3119 for (const ModelObject* object : model.objects) {
3120 ++count;
3121 const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
3122 if (!sla_support_points.empty()) {
3123 sprintf(buffer, "object_id=%d|", count);
3124 out += buffer;
3125
3126 // Store the layer height profile as a single space separated list.
3127 for (size_t i = 0; i < sla_support_points.size(); ++i) {
3128 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);
3129 out += buffer;
3130 }
3131 out += "\n";
3132 }
3133 }
3134
3135 if (!out.empty()) {
3136 // Adds version header at the beginning:
3137 out = std::string("support_points_format_version=") + std::to_string(support_points_format_version) + std::string("\n") + out;
3138
3139 if (!mz_zip_writer_add_mem(&archive, SLA_SUPPORT_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3140 add_error("Unable to add sla support points file to archive");
3141 return false;
3142 }
3143 }
3144 return true;
3145 }
const std::string SLA_SUPPORT_POINTS_FILE
Definition 3mf.cpp:80
@ support_points_format_version
Definition 3mf.hpp:20

References Slic3r::is_decimal_separator_point(), MZ_DEFAULT_COMPRESSION, mz_zip_writer_add_mem(), Slic3r::Model::objects, SLA_SUPPORT_POINTS_FILE, and Slic3r::support_points_format_version.

+ Here is the call graph for this function:

◆ _add_thumbnail_file_to_archive()

bool Slic3r::_3MF_Exporter::_add_thumbnail_file_to_archive ( mz_zip_archive archive,
const ThumbnailData thumbnail_data 
)
private
2549 {
2550 bool res = false;
2551
2552 size_t png_size = 0;
2553 void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1);
2554 if (png_data != nullptr) {
2555 res = mz_zip_writer_add_mem(&archive, THUMBNAIL_FILE.c_str(), (const void*)png_data, png_size, MZ_DEFAULT_COMPRESSION);
2556 mz_free(png_data);
2557 }
2558
2559 if (!res)
2560 add_error("Unable to add thumbnail file to archive");
2561
2562 return res;
2563 }
void * tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
Definition miniz.c:2102
void mz_free(void *p)
Definition miniz.c:155
@ MZ_DEFAULT_LEVEL
Definition miniz.h:237

References Slic3r::ThumbnailData::height, MZ_DEFAULT_COMPRESSION, MZ_DEFAULT_LEVEL, mz_free(), mz_zip_writer_add_mem(), Slic3r::ThumbnailData::pixels, tdefl_write_image_to_png_file_in_memory_ex(), THUMBNAIL_FILE, and Slic3r::ThumbnailData::width.

+ Here is the call graph for this function:

◆ _save_model_to_file()

bool Slic3r::_3MF_Exporter::_save_model_to_file ( const std::string &  filename,
Model model,
const DynamicPrintConfig config,
const ThumbnailData thumbnail_data 
)
private
2401 {
2402 mz_zip_archive archive;
2403 mz_zip_zero_struct(&archive);
2404
2405 if (!open_zip_writer(&archive, filename)) {
2406 add_error("Unable to open the file");
2407 return false;
2408 }
2409
2410 // Adds content types file ("[Content_Types].xml";).
2411 // The content of this file is the same for each PrusaSlicer 3mf.
2412 if (!_add_content_types_file_to_archive(archive)) {
2413 close_zip_writer(&archive);
2414 boost::filesystem::remove(filename);
2415 return false;
2416 }
2417
2418 if (thumbnail_data != nullptr && thumbnail_data->is_valid()) {
2419 // Adds the file Metadata/thumbnail.png.
2420 if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data)) {
2421 close_zip_writer(&archive);
2422 boost::filesystem::remove(filename);
2423 return false;
2424 }
2425 }
2426
2427 // Adds relationships file ("_rels/.rels").
2428 // The content of this file is the same for each PrusaSlicer 3mf.
2429 // The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
2430 if (!_add_relationships_file_to_archive(archive)) {
2431 close_zip_writer(&archive);
2432 boost::filesystem::remove(filename);
2433 return false;
2434 }
2435
2436 // Adds model file ("3D/3dmodel.model").
2437 // This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
2438 IdToObjectDataMap objects_data;
2439 if (!_add_model_file_to_archive(filename, archive, model, objects_data)) {
2440 close_zip_writer(&archive);
2441 boost::filesystem::remove(filename);
2442 return false;
2443 }
2444
2445 // Adds file with information for object cut ("Metadata/Slic3r_PE_cut_information.txt").
2446 // All information for object cut of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
2447 // The index differes from the index of an object ID of an object instance of a 3MF file!
2448 if (!_add_cut_information_file_to_archive(archive, model)) {
2449 close_zip_writer(&archive);
2450 boost::filesystem::remove(filename);
2451 return false;
2452 }
2453
2454 // Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
2455 // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
2456 // The index differes from the index of an object ID of an object instance of a 3MF file!
2457 if (!_add_layer_height_profile_file_to_archive(archive, model)) {
2458 close_zip_writer(&archive);
2459 boost::filesystem::remove(filename);
2460 return false;
2461 }
2462
2463 // Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
2464 // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
2465 // The index differes from the index of an object ID of an object instance of a 3MF file!
2466 if (!_add_layer_config_ranges_file_to_archive(archive, model)) {
2467 close_zip_writer(&archive);
2468 boost::filesystem::remove(filename);
2469 return false;
2470 }
2471
2472 // Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
2473 // All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
2474 // The index differes from the index of an object ID of an object instance of a 3MF file!
2475 if (!_add_sla_support_points_file_to_archive(archive, model)) {
2476 close_zip_writer(&archive);
2477 boost::filesystem::remove(filename);
2478 return false;
2479 }
2480
2481 if (!_add_sla_drain_holes_file_to_archive(archive, model)) {
2482 close_zip_writer(&archive);
2483 boost::filesystem::remove(filename);
2484 return false;
2485 }
2486
2487
2488 // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
2489 // All custom gcode per height of whole Model are stored here
2490 if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config)) {
2491 close_zip_writer(&archive);
2492 boost::filesystem::remove(filename);
2493 return false;
2494 }
2495
2496 // Adds slic3r print config file ("Metadata/Slic3r_PE.config").
2497 // This file contains the content of FullPrintConfing / SLAFullPrintConfig.
2498 if (config != nullptr) {
2499 if (!_add_print_config_file_to_archive(archive, *config)) {
2500 close_zip_writer(&archive);
2501 boost::filesystem::remove(filename);
2502 return false;
2503 }
2504 }
2505
2506 // Adds slic3r model config file ("Metadata/Slic3r_PE_model.config").
2507 // This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides).
2508 // As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data
2509 // is stored here as well.
2510 if (!_add_model_config_file_to_archive(archive, model, objects_data)) {
2511 close_zip_writer(&archive);
2512 boost::filesystem::remove(filename);
2513 return false;
2514 }
2515
2516 if (!mz_zip_writer_finalize_archive(&archive)) {
2517 close_zip_writer(&archive);
2518 boost::filesystem::remove(filename);
2519 add_error("Unable to finalize the archive");
2520 return false;
2521 }
2522
2523 close_zip_writer(&archive);
2524
2525 return true;
2526 }
bool _add_layer_height_profile_file_to_archive(mz_zip_archive &archive, Model &model)
Definition 3mf.cpp:3020
bool _add_thumbnail_file_to_archive(mz_zip_archive &archive, const ThumbnailData &thumbnail_data)
Definition 3mf.cpp:2548
bool _add_sla_support_points_file_to_archive(mz_zip_archive &archive, Model &model)
Definition 3mf.cpp:3112
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive &archive, Model &model)
Definition 3mf.cpp:3054
std::map< int, ObjectData > IdToObjectDataMap
Definition 3mf.cpp:2365
bool _add_sla_drain_holes_file_to_archive(mz_zip_archive &archive, Model &model)
Definition 3mf.cpp:3147
bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
Definition 3mf.cpp:2957
bool _add_print_config_file_to_archive(mz_zip_archive &archive, const DynamicPrintConfig &config)
Definition 3mf.cpp:3198
bool _add_model_file_to_archive(const std::string &filename, mz_zip_archive &archive, const Model &model, IdToObjectDataMap &objects_data)
Definition 3mf.cpp:2595
bool _add_relationships_file_to_archive(mz_zip_archive &archive)
Definition 3mf.cpp:2565
bool _add_model_config_file_to_archive(mz_zip_archive &archive, const Model &model, const IdToObjectDataMap &objects_data)
Definition 3mf.cpp:3219
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive &archive, Model &model, const DynamicPrintConfig *config)
Definition 3mf.cpp:3350
bool _add_content_types_file_to_archive(mz_zip_archive &archive)
Definition 3mf.cpp:2528
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
Definition miniz.c:7462
void mz_zip_zero_struct(mz_zip_archive *pZip)
Definition miniz.c:3792
Definition miniz.h:1053
bool close_zip_writer(mz_zip_archive *zip)
Definition miniz_extension.cpp:73
bool open_zip_writer(mz_zip_archive *zip, const std::string &fname)
Definition miniz_extension.cpp:67

References Slic3r::close_zip_writer(), Slic3r::ThumbnailData::is_valid(), mz_zip_writer_finalize_archive(), mz_zip_zero_struct(), and Slic3r::open_zip_writer().

+ Here is the call graph for this function:

◆ add_error()

void Slic3r::_3MF_Base::add_error ( const std::string &  error)
inlineprotectedinherited
305{ m_errors.push_back(error); }
std::vector< std::string > m_errors
Definition 3mf.cpp:302
static char error[256]
Definition tga.cpp:50

References error, and Slic3r::_3MF_Base::m_errors.

Referenced by Slic3r::_3MF_Importer::_extract_model_from_archive(), and Slic3r::_3MF_Importer::_load_model_from_file().

+ Here is the caller graph for this function:

◆ add_transformation()

void Slic3r::_3MF_Exporter::add_transformation ( std::stringstream &  stream,
const Transform3d tr 
)
static
2927 {
2928 for (unsigned c = 0; c < 4; ++c) {
2929 for (unsigned r = 0; r < 3; ++r) {
2930 stream << tr(r, c);
2931 if (r != 2 || c != 3) stream << " ";
2932 }
2933 }
2934 }

◆ clear_errors()

void Slic3r::_3MF_Base::clear_errors ( )
inlineprotectedinherited
306{ m_errors.clear(); }

References Slic3r::_3MF_Base::m_errors.

Referenced by Slic3r::_3MF_Importer::load_model_from_file().

+ Here is the caller graph for this function:

◆ log_errors()

void Slic3r::_3MF_Base::log_errors ( )
inlineinherited
310 {
311 for (const std::string& error : m_errors)
312 BOOST_LOG_TRIVIAL(error) << error;
313 }

References error, and Slic3r::_3MF_Base::m_errors.

Referenced by Slic3r::load_3mf(), and Slic3r::store_3mf().

+ Here is the caller graph for this function:

◆ save_model_to_file()

bool Slic3r::_3MF_Exporter::save_model_to_file ( const std::string &  filename,
Model model,
const DynamicPrintConfig config,
bool  fullpath_sources,
const ThumbnailData thumbnail_data,
bool  zip64 
)
2393 {
2394 clear_errors();
2395 m_fullpath_sources = fullpath_sources;
2396 m_zip64 = zip64;
2397 return _save_model_to_file(filename, model, config, thumbnail_data);
2398 }
void clear_errors()
Definition 3mf.cpp:306
bool _save_model_to_file(const std::string &filename, Model &model, const DynamicPrintConfig *config, const ThumbnailData *thumbnail_data)
Definition 3mf.cpp:2400

Referenced by Slic3r::store_3mf().

+ Here is the caller graph for this function:

Member Data Documentation

◆ m_errors

std::vector<std::string> Slic3r::_3MF_Base::m_errors
privateinherited

◆ m_fullpath_sources

bool Slic3r::_3MF_Exporter::m_fullpath_sources { true }
private

◆ m_zip64

bool Slic3r::_3MF_Exporter::m_zip64 { true }
private

The documentation for this class was generated from the following file: