OrcaSlicer latest
OrcaSlicer
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  Offsets
struct  ObjectData

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 ()

Protected Member Functions

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

Private Types

typedef std::map< const ModelVolume *, Offsets > VolumeToOffsetsMap
typedef std::vector< BuildItem > BuildItemsList
typedef std::map< int, ObjectData > IdToObjectDataMap

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_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
2934 {
2935 // This happens for empty projects
2936 if (build_items.size() == 0) {
2937 add_error("No build item found");
2938 return true;
2939 }
2940
2941 stream << " <" << BUILD_TAG << ">\n";
2942
2943 for (const BuildItem& item : build_items) {
2944 stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id << "\" " << TRANSFORM_ATTR << "=\"";
2945 for (unsigned c = 0; c < 4; ++c) {
2946 for (unsigned r = 0; r < 3; ++r) {
2947 stream << item.transform(r, c);
2948 if (r != 2 || c != 3)
2949 stream << " ";
2950 }
2951 }
2952 stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
2953 }
2954
2955 stream << " </" << BUILD_TAG << ">\n";
2956
2957 return true;
2958 }
static constexpr const char * BUILD_TAG
Definition 3mf.cpp:119
static constexpr const char * ITEM_TAG
Definition 3mf.cpp:120
static constexpr const char * PRINTABLE_ATTR
Definition 3mf.cpp:138
static constexpr const char * TRANSFORM_ATTR
Definition 3mf.cpp:137
static constexpr const char * OBJECTID_ATTR
Definition 3mf.cpp:136
void add_error(const std::string &error)
Definition 3mf.cpp:423
Definition 3mf.cpp:2333

References _add_build_to_model_stream(), Slic3r::_3MF_Base::add_error(), BUILD_TAG, ITEM_TAG, OBJECTID_ATTR, PRINTABLE_ATTR, and TRANSFORM_ATTR.

Referenced by _add_build_to_model_stream(), and _add_model_file_to_archive().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _add_content_types_file_to_archive()

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

References _add_content_types_file_to_archive(), Slic3r::_3MF_Base::add_error(), and CONTENT_TYPES_FILE.

Referenced by _add_content_types_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
3283{
3284 return true;
3285 //std::string out = "";
3286
3287 //if (!model.custom_gcode_per_print_z.gcodes.empty()) {
3288 // pt::ptree tree;
3289 // pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
3290
3291 // for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
3292 // pt::ptree& code_tree = main_tree.add("code", "");
3293
3294 // // store data of custom_gcode_per_print_z
3295 // code_tree.put("<xmlattr>.print_z" , code.print_z );
3296 // code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
3297 // code_tree.put("<xmlattr>.extruder" , code.extruder );
3298 // code_tree.put("<xmlattr>.color" , code.color );
3299 // code_tree.put("<xmlattr>.extra" , code.extra );
3300
3301 // //BBS
3302 // std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
3303 // code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
3304 // code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
3305 // code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
3306 // code_tree.put("<xmlattr>.gcode" , gcode );
3307 // }
3308
3309 // pt::ptree& mode_tree = main_tree.add("mode", "");
3310 // // store mode of a custom_gcode_per_print_z
3311 // mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
3312 // model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
3313 // CustomGCode::MultiExtruderMode);
3314
3315 // if (!tree.empty()) {
3316 // std::ostringstream oss;
3317 // boost::property_tree::write_xml(oss, tree);
3318 // out = oss.str();
3319
3320 // // Post processing("beautification") of the output string
3321 // boost::replace_all(out, "><", ">\n<");
3322 // }
3323 //}
3324
3325 //if (!out.empty()) {
3326 // if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3327 // add_error("Unable to add custom Gcodes per print_z file to archive");
3328 // return false;
3329 // }
3330 //}
3331
3332 //return true;
3333}

References _add_custom_gcode_per_print_z_file_to_archive().

Referenced by _add_custom_gcode_per_print_z_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
2995 {
2996 std::string out = "";
2997 pt::ptree tree;
2998
2999 unsigned int object_cnt = 0;
3000 for (const ModelObject* object : model.objects) {
3001 object_cnt++;
3002 const t_layer_config_ranges& ranges = object->layer_config_ranges;
3003 if (!ranges.empty())
3004 {
3005 pt::ptree& obj_tree = tree.add("objects.object","");
3006
3007 obj_tree.put("<xmlattr>.id", object_cnt);
3008
3009 // Store the layer config ranges.
3010 for (const auto& range : ranges) {
3011 pt::ptree& range_tree = obj_tree.add("range", "");
3012
3013 // store minX and maxZ
3014 range_tree.put("<xmlattr>.min_z", range.first.first);
3015 range_tree.put("<xmlattr>.max_z", range.first.second);
3016
3017 // store range configuration
3018 const ModelConfig& config = range.second;
3019 for (const std::string& opt_key : config.keys()) {
3020 pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
3021 opt_tree.put("<xmlattr>.opt_key", opt_key);
3022 }
3023 }
3024 }
3025 }
3026
3027 if (!tree.empty()) {
3028 std::ostringstream oss;
3029 pt::write_xml(oss, tree);
3030 out = oss.str();
3031
3032 // Post processing("beautification") of the output string for a better preview
3033 boost::replace_all(out, "><object", ">\n <object");
3034 boost::replace_all(out, "><range", ">\n <range");
3035 boost::replace_all(out, "><option", ">\n <option");
3036 boost::replace_all(out, "></range>", ">\n </range>");
3037 boost::replace_all(out, "></object>", ">\n </object>");
3038 // OR just
3039 boost::replace_all(out, "><", ">\n<");
3040 }
3041
3042 if (!out.empty()) {
3043 if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3044 add_error("Unable to add layer heights profile file to archive");
3045 return false;
3046 }
3047 }
3048
3049 return true;
3050 }
const std::string LAYER_CONFIG_RANGES_FILE
Definition 3mf.cpp:104
auto range(Cont &&cont)
Definition libslic3r.h:405
std::map< t_layer_height_range, ModelConfig > t_layer_config_ranges
Definition Slicing.hpp:160

References _add_layer_config_ranges_file_to_archive(), Slic3r::_3MF_Base::add_error(), Slic3r::ModelConfig::keys(), LAYER_CONFIG_RANGES_FILE, Slic3r::Model::objects, Slic3r::ModelConfig::opt_serialize(), and Slic3r::range().

Referenced by _add_layer_config_ranges_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
2961 {
2963 std::string out = "";
2964 char buffer[1024];
2965
2966 unsigned int count = 0;
2967 for (const ModelObject* object : model.objects) {
2968 ++count;
2969 const std::vector<double>& layer_height_profile = object->layer_height_profile.get();
2970 if (layer_height_profile.size() >= 4 && layer_height_profile.size() % 2 == 0) {
2971 sprintf(buffer, "object_id=%d|", count);
2972 out += buffer;
2973
2974 // Store the layer height profile as a single semicolon separated list.
2975 for (size_t i = 0; i < layer_height_profile.size(); ++i) {
2976 sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
2977 out += buffer;
2978 }
2979
2980 out += "\n";
2981 }
2982 }
2983
2984 if (!out.empty()) {
2985 if (!mz_zip_writer_add_mem(&archive, LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
2986 add_error("Unable to add layer heights profile file to archive");
2987 return false;
2988 }
2989 }
2990
2991 return true;
2992 }
const std::string LAYER_HEIGHTS_PROFILE_FILE
Definition 3mf.cpp:103
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
bool is_decimal_separator_point()
Definition LocalesUtils.cpp:78

References _add_layer_height_profile_file_to_archive(), Slic3r::_3MF_Base::add_error(), count, Slic3r::is_decimal_separator_point(), LAYER_HEIGHTS_PROFILE_FILE, and Slic3r::Model::objects.

Referenced by _add_layer_height_profile_file_to_archive(), and _save_model_to_file().

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

References _add_mesh_to_object_stream(), Slic3r::_3MF_Base::add_error(), Slic3r::cast(), CUSTOM_SEAM_ATTR, CUSTOM_SUPPORTS_ATTR, Slic3r::f(), FUZZY_SKIN_ATTR, Slic3r::is_decimal_separator_point(), MESH_TAG, MMU_SEGMENTATION_ATTR, TRIANGLE_TAG, TRIANGLES_TAG, VERTEX_TAG, and VERTICES_TAG.

Referenced by _add_mesh_to_object_stream(), and _add_object_to_model_stream().

Here is the call graph for this function:
Here is the caller 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
3160 {
3161 std::stringstream stream;
3162 // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
3163 // when loaded as accurately as possible.
3164 stream << std::setprecision(std::numeric_limits<double>::max_digits10);
3165 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
3166 stream << "<" << CONFIG_TAG << ">\n";
3167
3168 for (const IdToObjectDataMap::value_type& obj_metadata : objects_data) {
3169 const ModelObject* obj = obj_metadata.second.object;
3170 if (obj != nullptr) {
3171 // Output of instances count added because of github #3435, currently not used by PrusaSlicer
3172 stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
3173
3174 // stores object's name
3175 if (!obj->name.empty())
3176 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
3177
3178 // stores object's config data
3179 for (const std::string& key : obj->config.keys()) {
3180 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
3181 }
3182
3183 for (const ModelVolume* volume : obj_metadata.second.object->volumes) {
3184 if (volume != nullptr) {
3185 const VolumeToOffsetsMap& offsets = obj_metadata.second.volumes_offsets;
3186 VolumeToOffsetsMap::const_iterator it = offsets.find(volume);
3187 if (it != offsets.end()) {
3188 // stores volume's offsets
3189 stream << " <" << VOLUME_TAG << " ";
3190 stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
3191 stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\">\n";
3192
3193 // stores volume's name
3194 if (!volume->name.empty())
3195 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
3196
3197 // stores volume's modifier field (legacy, to support old slicers)
3198 // Readers with only the legacy flag still see helper geometry as a modifier.
3199 if (volume->is_modifier() || volume->is_precise_seam())
3200 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
3201 // This Prusa-format reader uses ParameterModifier, not Bambu's modifier_part.
3202 // The base type overrides the legacy flag, so it must also be backward-compatible.
3203 // Use the same spelling for ordinary modifiers, including a downgraded seam helper.
3204 const bool store_as_modifier = volume->is_modifier() || volume->is_precise_seam();
3205 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
3206 VALUE_ATTR << "=\"" << (store_as_modifier ? "ParameterModifier" : ModelVolume::type_to_string(volume->type())) << "\"/>\n";
3207 if (volume->is_precise_seam())
3208 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << PRECISE_SEAM_TYPE_KEY << "\" " <<
3209 VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
3210
3211 // stores volume's local matrix
3212 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
3213 Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
3214 for (int r = 0; r < 4; ++r) {
3215 for (int c = 0; c < 4; ++c) {
3216 stream << matrix(r, c);
3217 if (r != 3 || c != 3)
3218 stream << " ";
3219 }
3220 }
3221 stream << "\"/>\n";
3222
3223 // stores volume's source data
3224 {
3225 std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
3226 std::string prefix = std::string(" <") + METADATA_TAG + " " + TYPE_ATTR + "=\"" + VOLUME_TYPE + "\" " + KEY_ATTR + "=\"";
3227 if (! volume->source.input_file.empty()) {
3228 stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
3229 stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
3230 stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
3231 stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
3232 stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
3233 stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
3234 }
3235 assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
3236 if (volume->source.is_converted_from_inches)
3237 stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
3238 else if (volume->source.is_converted_from_meters)
3239 stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n";
3240 }
3241
3242 // stores volume's config data
3243 for (const std::string& key : volume->config.keys()) {
3244 // Seam settings are inactive but must survive changing the helper back into a part/modifier.
3245 const bool dormant = volume->is_precise_seam();
3246 const std::string stored_key = dormant ? PRECISE_SEAM_CONFIG_PREFIX + key : key;
3247 const std::string value = volume->config.opt_serialize(key);
3248 // Config serialization is C-style, not XML: escape active settings too, including tabs.
3249 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << stored_key << "\" " << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(value) << "\"/>\n";
3250 }
3251
3252 // stores mesh's statistics
3253 const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
3254 stream << " <" << MESH_TAG << " ";
3255 stream << MESH_STAT_EDGES_FIXED << "=\"" << stats.edges_fixed << "\" ";
3256 stream << MESH_STAT_DEGENERATED_FACETS << "=\"" << stats.degenerate_facets << "\" ";
3257 stream << MESH_STAT_FACETS_REMOVED << "=\"" << stats.facets_removed << "\" ";
3258 stream << MESH_STAT_FACETS_RESERVED << "=\"" << stats.facets_reversed << "\" ";
3259 stream << MESH_STAT_BACKWARDS_EDGES << "=\"" << stats.backwards_edges << "\"/>\n";
3260
3261 stream << " </" << VOLUME_TAG << ">\n";
3262 }
3263 }
3264 }
3265
3266 stream << " </" << OBJECT_TAG << ">\n";
3267 }
3268 }
3269
3270 stream << "</" << CONFIG_TAG << ">\n";
3271
3272 std::string out = stream.str();
3273
3274 if (!mz_zip_writer_add_mem(&archive, MODEL_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3275 add_error("Unable to add model config file to archive");
3276 return false;
3277 }
3278
3279 return true;
3280 }
static constexpr const char * VOLUME_TYPE
Definition 3mf.cpp:151
static constexpr const char * SOURCE_VOLUME_ID_KEY
Definition 3mf.cpp:163
static constexpr const char * MESH_STAT_BACKWARDS_EDGES
Definition 3mf.cpp:174
static constexpr const char * NAME_KEY
Definition 3mf.cpp:153
static constexpr const char * SOURCE_IN_METERS
Definition 3mf.cpp:168
static constexpr const char * OBJECT_TYPE
Definition 3mf.cpp:150
static constexpr const char * FIRST_TRIANGLE_ID_ATTR
Definition 3mf.cpp:147
static constexpr const char * VOLUME_TAG
Definition 3mf.cpp:124
static constexpr const char * MODIFIER_KEY
Definition 3mf.cpp:154
static constexpr const char * MESH_STAT_EDGES_FIXED
Definition 3mf.cpp:170
static constexpr const char * SOURCE_OFFSET_Z_KEY
Definition 3mf.cpp:166
static constexpr const char * SOURCE_OFFSET_X_KEY
Definition 3mf.cpp:164
static constexpr const char * SOURCE_IN_INCHES
Definition 3mf.cpp:167
static constexpr const char * LAST_TRIANGLE_ID_ATTR
Definition 3mf.cpp:148
static constexpr const char * SOURCE_OBJECT_ID_KEY
Definition 3mf.cpp:162
static constexpr const char * MESH_STAT_FACETS_RESERVED
Definition 3mf.cpp:173
static constexpr const char * CONFIG_TAG
Definition 3mf.cpp:123
static constexpr char PRECISE_SEAM_CONFIG_PREFIX[]
Definition 3mf.cpp:159
static constexpr const char * INSTANCESCOUNT_ATTR
Definition 3mf.cpp:139
static constexpr const char * MESH_STAT_FACETS_REMOVED
Definition 3mf.cpp:172
static constexpr const char * VALUE_ATTR
Definition 3mf.cpp:146
static constexpr const char * TYPE_ATTR
Definition 3mf.cpp:128
static constexpr const char * SOURCE_OFFSET_Y_KEY
Definition 3mf.cpp:165
static constexpr const char * MATRIX_KEY
Definition 3mf.cpp:160
const std::string MODEL_CONFIG_FILE
Definition 3mf.cpp:102
static constexpr const char * SOURCE_FILE_KEY
Definition 3mf.cpp:161
static constexpr const char * ID_ATTR
Definition 3mf.cpp:129
static constexpr const char * VOLUME_TYPE_KEY
Definition 3mf.cpp:155
static constexpr const char * KEY_ATTR
Definition 3mf.cpp:145
static constexpr const char * METADATA_TAG
Definition 3mf.cpp:121
static constexpr const char * PRECISE_SEAM_TYPE_KEY
Definition 3mf.cpp:157
static constexpr const char * OBJECT_TAG
Definition 3mf.cpp:111
static constexpr const char * MESH_STAT_DEGENERATED_FACETS
Definition 3mf.cpp:171
static std::string type_to_string(const ModelVolumeType t)
Definition Model.cpp:2942
std::map< const ModelVolume *, Offsets > VolumeToOffsetsMap
Definition 3mf.cpp:2360
bool m_fullpath_sources
Definition 3mf.cpp:2376
std::string xml_escape(std::string text, bool is_marked)
Definition utils.cpp:1586
std::string xml_escape_double_quotes_attribute_value(std::string text)
Definition utils.cpp:1616

References _add_model_config_file_to_archive(), Slic3r::_3MF_Base::add_error(), Slic3r::RepairedMeshErrors::backwards_edges, Slic3r::ModelObject::config, Slic3r::ModelVolume::config, CONFIG_TAG, Slic3r::RepairedMeshErrors::degenerate_facets, Slic3r::RepairedMeshErrors::edges_fixed, Slic3r::RepairedMeshErrors::facets_removed, Slic3r::RepairedMeshErrors::facets_reversed, FIRST_TRIANGLE_ID_ATTR, Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelVolume::get_matrix(), ID_ATTR, Slic3r::ModelVolume::Source::input_file, Slic3r::ModelObject::instances, INSTANCESCOUNT_ATTR, Slic3r::ModelVolume::Source::is_converted_from_inches, Slic3r::ModelVolume::Source::is_converted_from_meters, Slic3r::ModelVolume::is_modifier(), Slic3r::ModelVolume::is_precise_seam(), KEY_ATTR, Slic3r::ModelConfig::keys(), LAST_TRIANGLE_ID_ATTR, m_fullpath_sources, MATRIX_KEY, Slic3r::ModelVolume::mesh(), Slic3r::ModelVolume::Source::mesh_offset, 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, Slic3r::ModelObject::name, Slic3r::ModelVolume::name, NAME_KEY, Slic3r::ModelVolume::object, Slic3r::ModelVolume::Source::object_idx, OBJECT_TAG, OBJECT_TYPE, Slic3r::ModelConfig::opt_serialize(), PRECISE_SEAM_CONFIG_PREFIX, PRECISE_SEAM_TYPE_KEY, Slic3r::TriangleMeshStats::repaired_errors, Slic3r::ModelVolume::source, SOURCE_FILE_KEY, SOURCE_IN_INCHES, SOURCE_IN_METERS, SOURCE_OBJECT_ID_KEY, SOURCE_OFFSET_X_KEY, SOURCE_OFFSET_Y_KEY, SOURCE_OFFSET_Z_KEY, SOURCE_VOLUME_ID_KEY, Slic3r::TriangleMesh::stats(), Slic3r::ModelVolume::Source::transform, Slic3r::ModelVolume::type(), TYPE_ATTR, Slic3r::ModelVolume::type_to_string(), VALUE_ATTR, Slic3r::ModelVolume::Source::volume_idx, VOLUME_TAG, VOLUME_TYPE, VOLUME_TYPE_KEY, Slic3r::ModelObject::volumes, Slic3r::xml_escape(), and Slic3r::xml_escape_double_quotes_attribute_value().

Referenced by _add_model_config_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
2595 {
2596 mz_zip_writer_staged_context context;
2597 if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(),
2598 m_zip64 ?
2599 // Maximum expected and allowed 3MF file size is 16GiB.
2600 // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
2601 (uint64_t(1) << 30) * 16 :
2602 // Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
2603 // GH issue #6193.
2604 (uint64_t(1) << 32) - 1,
2605 nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0)) {
2606 add_error("Unable to add model file to archive");
2607 return false;
2608 }
2609
2610 {
2611 std::stringstream stream;
2612 reset_stream(stream);
2613 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2614 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";
2615 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
2616
2617 if (model.is_fdm_support_painted())
2618 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION << "\">" << FDM_SUPPORTS_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
2619
2620 if (model.is_seam_painted())
2621 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_SEAM_PAINTING_VERSION << "\">" << SEAM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
2622
2623 if (model.is_mm_painted())
2624 stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_MM_PAINTING_VERSION << "\">" << MM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
2625
2626 std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
2627 stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
2628 stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
2629 stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "</" << METADATA_TAG << ">\n";
2630 stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "</" << METADATA_TAG << ">\n";
2631 stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "</" << METADATA_TAG << ">\n";
2632 stream << " <" << METADATA_TAG << " name=\"Rating\">" << "</" << METADATA_TAG << ">\n";
2633 // Orca: PRIVACY: do not store creation & modification date in 3mf
2634 stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << "</" << METADATA_TAG << ">\n";
2635 stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << "</" << METADATA_TAG << ">\n";
2636 stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
2637 stream << " <" << RESOURCES_TAG << ">\n";
2638 std::string buf = stream.str();
2639 if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
2640 add_error("Unable to add model file to archive");
2641 return false;
2642 }
2643 }
2644
2645 // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
2646 BuildItemsList build_items;
2647
2648 // The object_id here is a one based identifier of the first instance of a ModelObject in the 3MF file, where
2649 // all the object instances of all ModelObjects are stored and indexed in a 1 based linear fashion.
2650 // Therefore the list of object_ids here may not be continuous.
2651 unsigned int object_id = 1;
2652 for (ModelObject* obj : model.objects) {
2653 if (obj == nullptr)
2654 continue;
2655
2656 // Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
2657 unsigned int curr_id = object_id;
2658 IdToObjectDataMap::iterator object_it = objects_data.insert({ curr_id, ObjectData(obj) }).first;
2659 // Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
2660 // object_it->second.volumes_offsets will contain the offsets of the ModelVolumes in that single indexed triangle set.
2661 // object_id will be increased to point to the 1st instance of the next ModelObject.
2662 if (!_add_object_to_model_stream(context, object_id, *obj, build_items, object_it->second.volumes_offsets)) {
2663 add_error("Unable to add object to archive");
2664 mz_zip_writer_add_staged_finish(&context);
2665 return false;
2666 }
2667 }
2668
2669 {
2670 std::stringstream stream;
2671 reset_stream(stream);
2672 stream << " </" << RESOURCES_TAG << ">\n";
2673
2674 // Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
2675 if (!_add_build_to_model_stream(stream, build_items)) {
2676 add_error("Unable to add build to archive");
2677 mz_zip_writer_add_staged_finish(&context);
2678 return false;
2679 }
2680
2681 stream << "</" << MODEL_TAG << ">\n";
2682
2683 std::string buf = stream.str();
2684
2685 if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
2686 ! mz_zip_writer_add_staged_finish(&context)) {
2687 add_error("Unable to add model file to archive");
2688 return false;
2689 }
2690 }
2691
2692 return true;
2693 }
const unsigned int SEAM_PAINTING_VERSION
Definition 3mf.cpp:88
const std::string MODEL_FILE
Definition 3mf.cpp:97
const unsigned int VERSION_3MF
Definition 3mf.cpp:79
static constexpr const char * RESOURCES_TAG
Definition 3mf.cpp:110
const std::string SLIC3RPE_SEAM_PAINTING_VERSION
Definition 3mf.cpp:92
const std::string SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION
Definition 3mf.cpp:91
static constexpr const char * MODEL_TAG
Definition 3mf.cpp:109
const unsigned int FDM_SUPPORTS_PAINTING_VERSION
Definition 3mf.cpp:87
const std::string SLIC3RPE_MM_PAINTING_VERSION
Definition 3mf.cpp:93
const unsigned int MM_PAINTING_VERSION
Definition 3mf.cpp:89
const char * SLIC3RPE_3MF_VERSION
Definition 3mf.cpp:82
bool m_zip64
Definition 3mf.cpp:2377
bool _add_build_to_model_stream(std::stringstream &stream, const BuildItemsList &build_items)
Definition 3mf.cpp:2933
std::vector< BuildItem > BuildItemsList
Definition 3mf.cpp:2373
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:2695
static void reset_stream(std::stringstream &stream)
Definition 3mf.cpp:2583
Definition 3mf.cpp:2363

References _add_build_to_model_stream(), _add_model_file_to_archive(), _add_object_to_model_stream(), Slic3r::_3MF_Base::add_error(), FDM_SUPPORTS_PAINTING_VERSION, Slic3r::Model::is_fdm_support_painted(), Slic3r::Model::is_mm_painted(), Slic3r::Model::is_seam_painted(), m_zip64, METADATA_TAG, MM_PAINTING_VERSION, MODEL_FILE, MODEL_TAG, 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, VERSION_3MF, and Slic3r::xml_escape().

Referenced by _add_model_file_to_archive(), and _save_model_to_file().

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

References _add_mesh_to_object_stream(), _add_object_to_model_stream(), Slic3r::_3MF_Base::add_error(), COMPONENT_TAG, COMPONENTS_TAG, OBJECT_TAG, and Slic3r::reset_stream().

Referenced by _add_model_file_to_archive(), and _add_object_to_model_stream().

Here is the call graph for this function:
Here is the caller 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
3139 {
3141 char buffer[1024];
3142 sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
3143 std::string out = buffer;
3144
3145 for (const std::string &key : config.keys())
3146 if (key != "compatible_printers")
3147 out += "; " + key + " = " + config.opt_serialize(key) + "\n";
3148
3149 if (!out.empty()) {
3150 if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3151 add_error("Unable to add print config file to archive");
3152 return false;
3153 }
3154 }
3155
3156 return true;
3157 }
const std::string PRINT_CONFIG_FILE
Definition 3mf.cpp:101
std::string header_slic3r_generated()
Definition utils.cpp:1470

References _add_print_config_file_to_archive(), Slic3r::_3MF_Base::add_error(), Slic3r::header_slic3r_generated(), Slic3r::is_decimal_separator_point(), Slic3r::DynamicConfig::keys(), Slic3r::ConfigBase::opt_serialize(), and PRINT_CONFIG_FILE.

Referenced by _add_print_config_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _add_relationships_file_to_archive()

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

References _add_relationships_file_to_archive(), Slic3r::_3MF_Base::add_error(), MODEL_FILE, RELATIONSHIPS_FILE, and THUMBNAIL_FILE.

Referenced by _add_relationships_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
3088 {
3090 const char *const fmt = "object_id=%d|";
3091 std::string out;
3092
3093 unsigned int count = 0;
3094 for (const ModelObject* object : model.objects) {
3095 ++count;
3096 sla::DrainHoles drain_holes = object->sla_drain_holes;
3097
3098 // The holes were placed 1mm above the mesh in the first implementation.
3099 // This was a bad idea and the reference point was changed in 2.3 so
3100 // to be on the mesh exactly. The elevated position is still saved
3101 // in 3MFs for compatibility reasons.
3102 for (sla::DrainHole& hole : drain_holes) {
3103 hole.pos -= hole.normal.normalized();
3104 hole.height += 1.f;
3105 }
3106
3107 if (!drain_holes.empty()) {
3108 out += string_printf(fmt, count);
3109
3110 // Store the layer height profile as a single space separated list.
3111 for (size_t i = 0; i < drain_holes.size(); ++i)
3112 out += string_printf((i == 0 ? "%f %f %f %f %f %f %f %f" : " %f %f %f %f %f %f %f %f"),
3113 drain_holes[i].pos(0),
3114 drain_holes[i].pos(1),
3115 drain_holes[i].pos(2),
3116 drain_holes[i].normal(0),
3117 drain_holes[i].normal(1),
3118 drain_holes[i].normal(2),
3119 drain_holes[i].radius,
3120 drain_holes[i].height);
3121
3122 out += "\n";
3123 }
3124 }
3125
3126 if (!out.empty()) {
3127 // Adds version header at the beginning:
3128 out = std::string("drain_holes_format_version=") + std::to_string(drain_holes_format_version) + std::string("\n") + out;
3129
3130 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))) {
3131 add_error("Unable to add sla support points file to archive");
3132 return false;
3133 }
3134 }
3135 return true;
3136 }
const std::string SLA_DRAIN_HOLES_FILE
Definition 3mf.cpp:106
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
std::vector< DrainHole > DrainHoles
Definition Hollowing.hpp:74
@ drain_holes_format_version
Definition 3mf.hpp:48
std::string string_printf(const char *format,...)
Definition utils.cpp:1448
Point normal(Point pt, double scale)
Definition TreeSupport.cpp:112

References _add_sla_drain_holes_file_to_archive(), Slic3r::_3MF_Base::add_error(), count, Slic3r::drain_holes_format_version, Slic3r::is_decimal_separator_point(), Slic3r::normal(), Slic3r::Model::objects, SLA_DRAIN_HOLES_FILE, and Slic3r::string_printf().

Referenced by _add_sla_drain_holes_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
3053 {
3055 std::string out = "";
3056 char buffer[1024];
3057
3058 unsigned int count = 0;
3059 for (const ModelObject* object : model.objects) {
3060 ++count;
3061 const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
3062 if (!sla_support_points.empty()) {
3063 sprintf(buffer, "object_id=%d|", count);
3064 out += buffer;
3065
3066 // Store the layer height profile as a single space separated list.
3067 for (size_t i = 0; i < sla_support_points.size(); ++i) {
3068 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);
3069 out += buffer;
3070 }
3071 out += "\n";
3072 }
3073 }
3074
3075 if (!out.empty()) {
3076 // Adds version header at the beginning:
3077 out = std::string("support_points_format_version=") + std::to_string(support_points_format_version) + std::string("\n") + out;
3078
3079 if (!mz_zip_writer_add_mem(&archive, SLA_SUPPORT_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
3080 add_error("Unable to add sla support points file to archive");
3081 return false;
3082 }
3083 }
3084 return true;
3085 }
const std::string SLA_SUPPORT_POINTS_FILE
Definition 3mf.cpp:105
@ support_points_format_version
Definition 3mf.hpp:44

References _add_sla_support_points_file_to_archive(), Slic3r::_3MF_Base::add_error(), count, Slic3r::is_decimal_separator_point(), Slic3r::Model::objects, SLA_SUPPORT_POINTS_FILE, and Slic3r::support_points_format_version.

Referenced by _add_sla_support_points_file_to_archive(), and _save_model_to_file().

Here is the call graph for this function:
Here is the caller 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
2548 {
2549 bool res = false;
2550
2551 size_t png_size = 0;
2552 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);
2553 if (png_data != nullptr) {
2554 res = mz_zip_writer_add_mem(&archive, THUMBNAIL_FILE.c_str(), (const void*)png_data, png_size, MZ_DEFAULT_COMPRESSION);
2555 mz_free(png_data);
2556 }
2557
2558 if (!res)
2559 add_error("Unable to add thumbnail file to archive");
2560
2561 return res;
2562 }

References _add_thumbnail_file_to_archive(), Slic3r::_3MF_Base::add_error(), Slic3r::ThumbnailData::height, Slic3r::ThumbnailData::pixels, THUMBNAIL_FILE, and Slic3r::ThumbnailData::width.

Referenced by _add_thumbnail_file_to_archive(), and _save_model_to_file().

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

References _add_content_types_file_to_archive(), _add_custom_gcode_per_print_z_file_to_archive(), _add_layer_config_ranges_file_to_archive(), _add_layer_height_profile_file_to_archive(), _add_model_config_file_to_archive(), _add_model_file_to_archive(), _add_print_config_file_to_archive(), _add_relationships_file_to_archive(), _add_sla_drain_holes_file_to_archive(), _add_sla_support_points_file_to_archive(), _add_thumbnail_file_to_archive(), _save_model_to_file(), Slic3r::_3MF_Base::add_error(), Slic3r::close_zip_writer(), Slic3r::ThumbnailData::is_valid(), and Slic3r::open_zip_writer().

Referenced by _save_model_to_file(), and save_model_to_file().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ add_error()

void Slic3r::_3MF_Base::add_error ( const std::string & error)
inlineprotectedinherited
423{ m_errors.push_back(error); }
std::vector< std::string > m_errors
Definition 3mf.cpp:420

References m_errors.

Referenced by Slic3r::_3MF_Exporter::_add_build_to_model_stream(), Slic3r::_3MF_Exporter::_add_content_types_file_to_archive(), Slic3r::_3MF_Exporter::_add_layer_config_ranges_file_to_archive(), Slic3r::_3MF_Exporter::_add_layer_height_profile_file_to_archive(), Slic3r::_3MF_Exporter::_add_mesh_to_object_stream(), Slic3r::_3MF_Exporter::_add_model_config_file_to_archive(), Slic3r::_3MF_Exporter::_add_model_file_to_archive(), Slic3r::_3MF_Exporter::_add_object_to_model_stream(), Slic3r::_3MF_Exporter::_add_print_config_file_to_archive(), Slic3r::_3MF_Exporter::_add_relationships_file_to_archive(), Slic3r::_3MF_Exporter::_add_sla_drain_holes_file_to_archive(), Slic3r::_3MF_Exporter::_add_sla_support_points_file_to_archive(), Slic3r::_3MF_Exporter::_add_thumbnail_file_to_archive(), Slic3r::_3MF_Importer::_create_object_instance(), Slic3r::_3MF_Importer::_extract_layer_config_ranges_from_archive(), Slic3r::_3MF_Importer::_extract_layer_heights_profile_config_from_archive(), Slic3r::_3MF_Importer::_extract_model_config_from_archive(), Slic3r::_3MF_Importer::_extract_model_from_archive(), Slic3r::_3MF_Importer::_extract_print_config_from_archive(), Slic3r::_3MF_Importer::_extract_sla_drain_holes_from_archive(), Slic3r::_3MF_Importer::_extract_sla_support_points_from_archive(), Slic3r::_3MF_Importer::_generate_volumes(), Slic3r::_3MF_Importer::_handle_end_model(), Slic3r::_3MF_Importer::_handle_end_object(), Slic3r::_3MF_Importer::_handle_start_component(), Slic3r::_3MF_Importer::_handle_start_config_metadata(), Slic3r::_3MF_Importer::_handle_start_config_object(), Slic3r::_3MF_Importer::_handle_start_config_volume(), Slic3r::_3MF_Importer::_handle_start_config_volume_mesh(), Slic3r::_3MF_Importer::_handle_start_object(), Slic3r::_3MF_Importer::_load_model_from_file(), and Slic3r::_3MF_Exporter::_save_model_to_file().

Here is the caller graph for this function:

◆ clear_errors()

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

References m_errors.

Referenced by Slic3r::_3MF_Importer::load_model_from_file(), and Slic3r::_3MF_Exporter::save_model_to_file().

Here is the caller graph for this function:

◆ log_errors()

void Slic3r::_3MF_Base::log_errors ( )
inlineinherited
428 {
429 for (const std::string& error : m_errors)
430 BOOST_LOG_TRIVIAL(error) << error;
431 }

References 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 )
2401 {
2402 clear_errors();
2403 m_fullpath_sources = fullpath_sources;
2404 m_zip64 = zip64;
2405 return _save_model_to_file(filename, model, config, thumbnail_data);
2406 }
void clear_errors()
Definition 3mf.cpp:424
bool _save_model_to_file(const std::string &filename, Model &model, const DynamicPrintConfig *config, const ThumbnailData *thumbnail_data)
Definition 3mf.cpp:2408

References _save_model_to_file(), Slic3r::_3MF_Base::clear_errors(), m_fullpath_sources, m_zip64, and save_model_to_file().

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

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_errors

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

Referenced by add_error(), clear_errors(), and log_errors().

◆ m_fullpath_sources

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

Referenced by _add_model_config_file_to_archive(), and save_model_to_file().

◆ m_zip64

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

Referenced by _add_model_file_to_archive(), and save_model_to_file().


The documentation for this class was generated from the following file:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Format/3mf.cpp