OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::_BBS_3MF_Exporter Class Reference
Inheritance diagram for Slic3r::_BBS_3MF_Exporter:
Collaboration diagram for Slic3r::_BBS_3MF_Exporter:

Classes

struct  BuildItem
struct  ObjectData

Public Member Functions

bool save_model_to_file (StoreParams &store_params)
bool save_object_mesh (const std::string &temp_path, ModelObject const &object, int obj_id)
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) const
void clear_errors ()

Private Types

typedef std::map< const ModelVolume *, int > VolumeToObjectIDMap
typedef std::vector< BuildItem > BuildItemsList
typedef std::map< ModelObject const *, ObjectData > ObjectToObjectDataMap

Private Member Functions

bool _save_model_to_file (const std::string &filename, Model &model, PlateDataPtrs &plate_data_list, std::vector< Preset * > &project_presets, const DynamicPrintConfig *config, const std::vector< ThumbnailData * > &thumbnail_data, const std::vector< ThumbnailData * > &no_light_thumbnail_data, const std::vector< ThumbnailData * > &top_thumbnail_data, const std::vector< ThumbnailData * > &pick_thumbnail_data, Export3mfProgressFn proFn, const std::vector< ThumbnailData * > &calibration_data, const std::vector< PlateBBoxData * > &id_bboxes, BBLProject *project=nullptr, int export_plate_idx=-1)
bool _add_file_to_archive (mz_zip_archive &archive, const std::string &path_in_zip, const std::string &file_path)
bool _add_content_types_file_to_archive (mz_zip_archive &archive)
bool _add_thumbnail_file_to_archive (mz_zip_archive &archive, const ThumbnailData &thumbnail_data, const char *local_path, int index, bool generate_small_thumbnail=false)
bool _add_calibration_file_to_archive (mz_zip_archive &archive, const ThumbnailData &thumbnail_data, int index)
bool _add_bbox_file_to_archive (mz_zip_archive &archive, const PlateBBoxData &id_bboxes, int index)
bool _add_relationships_file_to_archive (mz_zip_archive &archive, std::string const &from={}, std::vector< std::string > const &targets={}, std::vector< std::string > const &types={}, PackingTemporaryData data=PackingTemporaryData(), int export_plate_idx=-1, bool plate_thumbnail_written=false, bool plate_small_thumbnail_written=false) const
bool _add_model_file_to_archive (const std::string &filename, mz_zip_archive &archive, const Model &model, ObjectToObjectDataMap &objects_data, Export3mfProgressFn proFn=nullptr, BBLProject *project=nullptr) const
bool _add_object_to_model_stream (mz_zip_writer_staged_context &context, ObjectData const &object_data) const
void _add_object_components_to_stream (std::stringstream &stream, ObjectData const &object_data) const
bool _add_mesh_to_object_stream (std::function< bool(std::string &, bool)> const &flush, ObjectData const &object_data) const
bool _add_build_to_model_stream (std::stringstream &stream, const BuildItemsList &build_items) const
bool _add_layer_height_profile_file_to_archive (mz_zip_archive &archive, Model &model)
bool _add_cad_recipe_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_brim_ear_points_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_project_config_file_to_archive (mz_zip_archive &archive, const DynamicPrintConfig &config, Model &model)
bool _add_project_embedded_presets_to_archive (mz_zip_archive &archive, Model &model, std::vector< Preset * > project_presets)
bool _add_model_config_file_to_archive (mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig &config, int export_plate_idx=-1, bool save_gcode=true, bool use_loaded_id=false)
bool _add_cut_information_file_to_archive (mz_zip_archive &archive, Model &model)
bool _add_slice_info_config_file_to_archive (mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig &config)
bool _add_filament_sequence_file_to_archive (mz_zip_archive &archive, const PlateDataPtrs &plate_data_list)
bool _add_gcode_file_to_archive (mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, Export3mfProgressFn proFn=nullptr)
bool _add_custom_gcode_per_print_z_file_to_archive (mz_zip_archive &archive, Model &model, const DynamicPrintConfig *config)
bool _add_auxiliary_dir_to_archive (mz_zip_archive &archive, const std::string &aux_dir, PackingTemporaryData &data)

Static Private Member Functions

static int convert_instance_id_to_resource_id (const Model &model, int obj_id, int instance_id)

Private Attributes

bool m_fullpath_sources { false }
bool m_zip64 { true }
bool m_production_ext { false }
bool m_skip_static { false }
bool m_from_backup_save { false }
bool m_split_model { false }
bool m_save_gcode { false }
bool m_skip_model { false }
bool m_skip_auxiliary { false }
bool m_minimal_published { false }
bool m_use_loaded_id { false }
bool m_share_mesh { false }
std::string m_thumbnail_middle = PRINTER_THUMBNAIL_MIDDLE_FILE
std::string m_thumbnail_small = PRINTER_THUMBNAIL_SMALL_FILE
std::map< void const *, std::pair< ObjectData *, ModelVolume const * > > m_shared_meshes
std::map< ModelVolume const *, std::pair< std::string, int > > m_volume_paths
boost::mutex mutex
std::vector< std::string > m_errors

Detailed Description


Class Documentation

◆ Slic3r::_BBS_3MF_Exporter::ObjectData

struct Slic3r::_BBS_3MF_Exporter::ObjectData
Collaboration diagram for Slic3r::_BBS_3MF_Exporter::ObjectData:
Class Members
int backup_id
ModelObject const * object
int object_id = 0
bool share_mesh = false
string sub_path
VolumeToObjectIDMap volumes_objectID

Member Typedef Documentation

◆ BuildItemsList

◆ ObjectToObjectDataMap

◆ VolumeToObjectIDMap

typedef std::map<const ModelVolume*, int> Slic3r::_BBS_3MF_Exporter::VolumeToObjectIDMap
private

Member Function Documentation

◆ _add_auxiliary_dir_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_auxiliary_dir_to_archive ( mz_zip_archive & archive,
const std::string & aux_dir,
PackingTemporaryData & data )
private
9137{
9138 bool result = true;
9139
9140 if (aux_dir.empty()) {
9141 //no accessory directories
9142 return result;
9143 }
9144
9145 boost::filesystem::path dir = boost::filesystem::path(aux_dir);
9146 if (!boost::filesystem::exists(dir))
9147 {
9148 //no accessory directories
9149 return result;
9150 }
9151
9152 static std::string const nocomp_exts[] = {".png", ".jpg", ".mp4", ".jpeg"};
9153 std::deque<boost::filesystem::path> directories({dir});
9154 int root_dir_len = dir.string().length() + 1;
9155 //boost file access
9156 while (!directories.empty()) {
9157 boost::system::error_code ec;
9158 boost::filesystem::directory_iterator iterator(directories.front(), ec);
9159 directories.pop_front();
9160 if (ec) continue;
9161 for (; iterator != end(iterator); iterator.increment(ec))
9162 {
9163 if (ec) break;
9164 auto dir_entry = *iterator;
9165 std::string src_file;
9166 std::string dst_in_3mf;
9167 if (boost::filesystem::is_directory(dir_entry.path(), ec))
9168 {
9169 directories.push_back(dir_entry.path());
9170 continue;
9171 }
9172 if (boost::filesystem::is_regular_file(dir_entry.path(), ec) && !m_skip_auxiliary)
9173 {
9174 src_file = dir_entry.path().string();
9175 dst_in_3mf = dir_entry.path().string();
9176 dst_in_3mf.replace(0, root_dir_len, AUXILIARY_DIR);
9177 std::replace(dst_in_3mf.begin(), dst_in_3mf.end(), '\\', '/');
9178 if (_3MF_COVER_FILE.compare(1, _3MF_COVER_FILE.length() - 1, dst_in_3mf) == 0) {
9179 data._3mf_thumbnail = dst_in_3mf;
9180 } else if (!m_thumbnail_small.empty() && m_thumbnail_small.compare(1, m_thumbnail_small.length() - 1, dst_in_3mf) == 0) {
9181 data._3mf_printer_thumbnail_small = dst_in_3mf;
9182 if (m_thumbnail_middle == m_thumbnail_small) data._3mf_printer_thumbnail_middle = dst_in_3mf;
9183 } else if (!m_thumbnail_middle.empty() && m_thumbnail_middle.compare(1, m_thumbnail_middle.length() - 1, dst_in_3mf) == 0) {
9184 data._3mf_printer_thumbnail_middle = dst_in_3mf;
9185 }
9186 result &= _add_file_to_archive(archive, dst_in_3mf, src_file);
9187 }
9188 }
9189 }
9190
9191 return result;
9192}
const std::string _3MF_COVER_FILE
Definition bbs_3mf.cpp:226
const std::string AUXILIARY_DIR
Definition bbs_3mf.cpp:245
std::string m_thumbnail_small
Definition bbs_3mf.cpp:6233
bool m_skip_auxiliary
Definition bbs_3mf.cpp:6228
bool _add_file_to_archive(mz_zip_archive &archive, const std::string &path_in_zip, const std::string &file_path)
Definition bbs_3mf.cpp:6912
std::string m_thumbnail_middle
Definition bbs_3mf.cpp:6232
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841

References _3MF_COVER_FILE, Slic3r::PackingTemporaryData::_3mf_printer_thumbnail_middle, Slic3r::PackingTemporaryData::_3mf_printer_thumbnail_small, Slic3r::PackingTemporaryData::_3mf_thumbnail, _add_auxiliary_dir_to_archive(), _add_file_to_archive(), AUXILIARY_DIR, m_skip_auxiliary, m_thumbnail_middle, and m_thumbnail_small.

Referenced by _add_auxiliary_dir_to_archive(), _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_bbox_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_bbox_file_to_archive ( mz_zip_archive & archive,
const PlateBBoxData & id_bboxes,
int index )
private
7046 {
7047 bool res = false;
7048 nlohmann::json j;
7049 id_bboxes.to_json(j);
7050 std::string out = j.dump();
7051
7052 std::string json_file_name = (boost::format(PATTERN_CONFIG_FILE_FORMAT) % (index + 1)).str();
7053 if (!mz_zip_writer_add_mem(&archive, json_file_name.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
7054 add_error("Unable to add json file to archive");
7055 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add json file to archive\n");
7056 return false;
7057 }
7058
7059 return true;
7060 }
#define PATTERN_CONFIG_FILE_FORMAT
Definition bbs_3mf.hpp:41
void add_error(const std::string &error) const
Definition bbs_3mf.cpp:816

References _add_bbox_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), PATTERN_CONFIG_FILE_FORMAT, and Slic3r::PlateBBoxData::to_json().

Referenced by _add_bbox_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_brim_ear_points_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_brim_ear_points_file_to_archive ( mz_zip_archive & archive,
Model & model )
private
8054 {
8055 std::string out = "";
8056 char buffer[1024];
8057
8058 unsigned int count = 0;
8059 for (const ModelObject* object : model.objects) {
8060 ++count;
8061 const BrimPoints& brim_points = object->brim_points;
8062 if (!brim_points.empty()) {
8063 sprintf(buffer, "object_id=%d|", count);
8064 out += buffer;
8065
8066 // Store the layer height profile as a single space separated list.
8067 for (size_t i = 0; i < brim_points.size(); ++i) {
8068 sprintf(buffer, (i==0 ? "%f %f %f %f" : " %f %f %f %f"), brim_points[i].pos(0), brim_points[i].pos(1), brim_points[i].pos(2), brim_points[i].head_front_radius);
8069 out += buffer;
8070 }
8071 out += "\n";
8072 }
8073 }
8074
8075 if (!out.empty()) {
8076 // Adds version header at the beginning:
8077 out = std::string("brim_points_format_version=") + std::to_string(brim_points_format_version) + std::string("\n") + out;
8078
8079 if (!mz_zip_writer_add_mem(&archive, BRIM_EAR_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
8080 add_error("Unable to add brim ear points file to archive");
8081 return false;
8082 }
8083 }
8084 return true;
8085 }
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
const std::string BRIM_EAR_POINTS_FILE
Definition bbs_3mf.cpp:241
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
@ brim_points_format_version
Definition bbs_3mf.hpp:214
std::vector< BrimPoint > BrimPoints
Definition BrimEarsPoint.hpp:66

References _add_brim_ear_points_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), BRIM_EAR_POINTS_FILE, Slic3r::brim_points_format_version, count, and Slic3r::Model::objects.

Referenced by _add_brim_ear_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_build_to_model_stream()

bool Slic3r::_BBS_3MF_Exporter::_add_build_to_model_stream ( std::stringstream & stream,
const BuildItemsList & build_items ) const
private
7855 {
7856 // This happens for empty projects
7857 if (build_items.size() == 0) {
7858 stream << " <" << BUILD_TAG << "/>\n";
7859 return true;
7860 }
7861
7862 stream << " <" << BUILD_TAG;
7863 if (m_production_ext)
7864 stream << " " << PUUID_ATTR << "=\"" << BUILD_UUID << "\"";
7865 stream << ">\n";
7866
7867 for (const BuildItem& item : build_items) {
7868 stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id;
7869 if (m_production_ext)
7870 stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{item.id} << BUILD_UUID_SUFFIX;
7871 if (!item.path.empty())
7872 stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path);
7873 stream << "\" " << TRANSFORM_ATTR << "=\"";
7874 add_transformation(stream, item.transform);
7875 stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
7876 }
7877
7878 stream << " </" << BUILD_TAG << ">\n";
7879
7880 return true;
7881 }
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
static constexpr const char * BUILD_UUID_SUFFIX
Definition bbs_3mf.cpp:333
static constexpr const char * BUILD_UUID
Definition bbs_3mf.cpp:332
static constexpr const char * PPATH_ATTR
Definition bbs_3mf.cpp:327
static constexpr const char * PUUID_ATTR
Definition bbs_3mf.cpp:325
bool m_production_ext
Definition bbs_3mf.cpp:6222
static void add_transformation(std::stringstream &stream, const Transform3d &tr)
Definition bbs_3mf.cpp:7844
std::string xml_escape(std::string text, bool is_marked)
Definition utils.cpp:1586
Definition bbs_3mf.cpp:6174

References _add_build_to_model_stream(), add_transformation(), BUILD_TAG, BUILD_UUID, BUILD_UUID_SUFFIX, ITEM_TAG, m_production_ext, OBJECTID_ATTR, PPATH_ATTR, PRINTABLE_ATTR, PUUID_ATTR, TRANSFORM_ATTR, and Slic3r::xml_escape().

Referenced by _add_build_to_model_stream().

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

◆ _add_cad_recipe_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_cad_recipe_file_to_archive ( mz_zip_archive & archive,
Model & model )
private
7982 {
7983 if (model.cad_recipe.empty())
7984 return true;
7985 if (!mz_zip_writer_add_mem(&archive, ORCA_CAD_RECIPE_FILE.c_str(),
7986 (const void*)model.cad_recipe.data(), model.cad_recipe.length(),
7987 MZ_DEFAULT_COMPRESSION)) {
7988 add_error("Unable to add CAD recipe file to archive");
7989 return false;
7990 }
7991 return true;
7992 }
const std::string ORCA_CAD_RECIPE_FILE
Definition bbs_3mf.cpp:236

References _add_cad_recipe_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::Model::cad_recipe, and ORCA_CAD_RECIPE_FILE.

Referenced by _add_cad_recipe_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_calibration_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_calibration_file_to_archive ( mz_zip_archive & archive,
const ThumbnailData & thumbnail_data,
int index )
private
7026 {
7027 bool res = false;
7028
7029 /*size_t png_size = 0;
7030 void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_COMPRESSION, 1);
7031 if (png_data != nullptr) {
7032 std::string thumbnail_name = (boost::format(PATTERN_FILE_FORMAT) % (index + 1)).str();
7033 res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)png_data, png_size, MZ_NO_COMPRESSION);
7034 mz_free(png_data);
7035 }
7036
7037 if (!res) {
7038 add_error("Unable to add thumbnail file to archive");
7039 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add thumbnail file to archive\n");
7040 }*/
7041
7042 return res;
7043 }

References _add_calibration_file_to_archive().

Referenced by _add_calibration_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_content_types_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_content_types_file_to_archive ( mz_zip_archive & archive)
private
6935 {
6936 std::stringstream stream;
6937 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
6938 stream << "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n";
6939 stream << " <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n";
6940 stream << " <Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\"/>\n";
6941 stream << " <Default Extension=\"png\" ContentType=\"image/png\"/>\n";
6942 stream << " <Default Extension=\"jpg\" ContentType=\"image/jpeg\"/>\n";
6943 stream << " <Default Extension=\"jpeg\" ContentType=\"image/jpeg\"/>\n";
6944 stream << " <Default Extension=\"json\" ContentType=\"application/json\"/>\n";
6945 stream << " <Default Extension=\"gcode\" ContentType=\"text/x.gcode\"/>\n";
6946 stream << "</Types>";
6947
6948 std::string out = stream.str();
6949
6950 if (!mz_zip_writer_add_mem(&archive, CONTENT_TYPES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
6951 add_error("Unable to add content types file to archive");
6952 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add content types file to archive\n");
6953 return false;
6954 }
6955
6956 return true;
6957 }
const std::string CONTENT_TYPES_FILE
Definition 3mf.cpp:98

References _add_content_types_file_to_archive(), Slic3r::_BBS_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::_BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive ( mz_zip_archive & archive,
Model & model,
const DynamicPrintConfig * config )
private
9080{
9081 //BBS: add plate tree related logic
9082 std::string out = "";
9083 bool has_custom_gcode = false;
9084 pt::ptree tree;
9085 pt::ptree& main_tree = tree.add("custom_gcodes_per_layer", "");
9086 for (auto custom_gcodes : model.plates_custom_gcodes) {
9087 has_custom_gcode = true;
9088 pt::ptree& plate_tree = main_tree.add("plate", "");
9089 pt::ptree& plate_idx_tree = plate_tree.add("plate_info", "");
9090 plate_idx_tree.put("<xmlattr>.id", custom_gcodes.first + 1);
9091
9092 // store data of custom_gcode_per_print_z
9093 for (const CustomGCode::Item& code : custom_gcodes.second.gcodes) {
9094 pt::ptree& code_tree = plate_tree.add("layer", "");
9095 code_tree.put("<xmlattr>.top_z", code.print_z);
9096 code_tree.put("<xmlattr>.type", static_cast<int>(code.type));
9097 code_tree.put("<xmlattr>.extruder", code.extruder);
9098 code_tree.put("<xmlattr>.color", code.color);
9099 code_tree.put("<xmlattr>.extra", code.extra);
9100
9101 //BBS
9102 std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
9103 code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
9104 code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
9105 code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
9106 code_tree.put("<xmlattr>.gcode", gcode);
9107 }
9108
9109 pt::ptree& mode_tree = plate_tree.add("mode", "");
9110 // store mode of a custom_gcode_per_print_z
9111 mode_tree.put("<xmlattr>.value", custom_gcodes.second.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
9114
9115 }
9116 if (has_custom_gcode) {
9117 std::ostringstream oss;
9118 boost::property_tree::write_xml(oss, tree);
9119 out = oss.str();
9120
9121 // Post processing("beautification") of the output string
9122 boost::replace_all(out, "><", ">\n<");
9123 }
9124
9125 if (!out.empty()) {
9126 if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
9127 add_error("Unable to add custom Gcodes per print_z file to archive");
9128 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add custom Gcodes per print_z file to archive\n");
9129 return false;
9130 }
9131 }
9132
9133 return true;
9134}
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE
Definition 3mf.cpp:107
static constexpr char SingleExtruderMode[]
Definition CustomGCode.hpp:64
static constexpr char MultiAsSingleMode[]
Definition CustomGCode.hpp:65
@ MultiAsSingle
Definition CustomGCode.hpp:57
@ SingleExtruder
Definition CustomGCode.hpp:56
static constexpr char MultiExtruderMode[]
Definition CustomGCode.hpp:66
@ PausePrint
Definition CustomGCode.hpp:20
@ ToolChange
Definition CustomGCode.hpp:21
@ Template
Definition CustomGCode.hpp:22
@ gcode
Definition Time.hpp:14

References _add_custom_gcode_per_print_z_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::CustomGCode::Item::color, CUSTOM_GCODE_PER_PRINT_Z_FILE, Slic3r::CustomGCode::Item::extra, Slic3r::CustomGCode::Item::extruder, Slic3r::CustomGCode::MultiAsSingle, Slic3r::CustomGCode::MultiAsSingleMode, Slic3r::CustomGCode::MultiExtruderMode, Slic3r::DynamicConfig::opt_string(), Slic3r::CustomGCode::PausePrint, Slic3r::Model::plates_custom_gcodes, Slic3r::CustomGCode::Item::print_z, Slic3r::CustomGCode::SingleExtruder, Slic3r::CustomGCode::SingleExtruderMode, Slic3r::CustomGCode::Template, Slic3r::CustomGCode::ToolChange, and Slic3r::CustomGCode::Item::type.

Referenced by _add_custom_gcode_per_print_z_file_to_archive(), _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_cut_information_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_cut_information_file_to_archive ( mz_zip_archive & archive,
Model & model )
private
7884 {
7885 std::string out = "";
7886 pt::ptree tree;
7887
7888 unsigned int object_cnt = 0;
7889 for (const ModelObject* object : model.objects) {
7890 object_cnt++;
7891 if (!object->is_cut())
7892 continue;
7893 pt::ptree& obj_tree = tree.add("objects.object", "");
7894
7895 obj_tree.put("<xmlattr>.id", object_cnt);
7896
7897 // Store info for cut_id
7898 pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
7899
7900 // store cut_id atributes
7901 cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
7902 cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
7903 cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
7904
7905 int volume_idx = -1;
7906 for (const ModelVolume* volume : object->volumes) {
7907 ++volume_idx;
7908 if (volume->is_cut_connector()) {
7909 pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
7910 connectors_tree.put("<xmlattr>.volume_id", volume_idx);
7911 connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
7912 connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
7913 connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
7914 }
7915 }
7916 }
7917
7918 if (!tree.empty()) {
7919 std::ostringstream oss;
7920 pt::write_xml(oss, tree);
7921 out = oss.str();
7922
7923 // Post processing("beautification") of the output string for a better preview
7924 boost::replace_all(out, "><object", ">\n <object");
7925 boost::replace_all(out, "><cut_id", ">\n <cut_id");
7926 boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
7927 boost::replace_all(out, "><connectors", ">\n <connectors");
7928 boost::replace_all(out, "></connectors>", ">\n </connectors>");
7929 boost::replace_all(out, "><connector", ">\n <connector");
7930 boost::replace_all(out, "></connector>", ">\n </connector>");
7931 boost::replace_all(out, "></object>", ">\n </object>");
7932 // OR just
7933 boost::replace_all(out, "><", ">\n<");
7934 }
7935
7936 if (!out.empty()) {
7937 if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
7938 add_error("Unable to add cut information file to archive");
7939 return false;
7940 }
7941 }
7942
7943 return true;
7944 }
const std::string CUT_INFORMATION_FILE
Definition bbs_3mf.cpp:250

References _add_cut_information_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::CutObjectBase::check_sum(), Slic3r::ModelVolume::CutInfo::connector_type, Slic3r::CutObjectBase::connectors_cnt(), Slic3r::ModelObject::cut_id, Slic3r::ModelVolume::cut_info, CUT_INFORMATION_FILE, Slic3r::ModelVolume::CutInfo::height_tolerance, Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::ModelObject::is_cut(), Slic3r::ModelVolume::is_cut_connector(), Slic3r::Model::objects, Slic3r::ModelVolume::CutInfo::radius_tolerance, and Slic3r::ModelObject::volumes.

Referenced by _add_cut_information_file_to_archive(), _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_filament_sequence_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_filament_sequence_file_to_archive ( mz_zip_archive & archive,
const PlateDataPtrs & plate_data_list )
private
8663 {
8664 nlohmann::json sequence_json;
8665
8666 for (size_t idx = 0; idx < plate_data_list.size(); ++idx) {
8667 const PlateData* plate_data = plate_data_list[idx];
8668 if (!plate_data)
8669 continue;
8670
8671 std::vector<unsigned int> filament_sequence = plate_data->filament_change_sequence;
8672 std::transform(filament_sequence.begin(), filament_sequence.end(), filament_sequence.begin(),
8673 [](unsigned int filament_id) { return filament_id + 1; });
8674
8675 const std::string plate_key = "plate_" + std::to_string(idx + 1);
8676 // Dynamic-map plates write the sequence under "filament_sequence"; every other plate (the
8677 // whole shipping fleet + H2C static mode) keeps the "sequence" key, so the saved 3mf is
8678 // byte-identical to the older format. The reader accepts both.
8679 const bool enable_dynamic_map = plate_data->nozzle_group_result && plate_data->nozzle_group_result->is_support_dynamic_nozzle_map();
8680 const std::string seq_key = enable_dynamic_map ? "filament_sequence" : "sequence";
8681 sequence_json[plate_key][seq_key] = filament_sequence;
8682 sequence_json[plate_key]["nozzle_sequence"] = plate_data->nozzle_change_sequence;
8683 sequence_json[plate_key]["optimal_assignment"] = plate_data->optimal_assignment;
8684 }
8685
8686 if (sequence_json.empty())
8687 return true;
8688
8689 const std::string out = sequence_json.dump();
8690 if (!mz_zip_writer_add_mem(&archive, FILAMENT_SEQUENCE_FILE.c_str(), out.c_str(), out.size(), MZ_DEFAULT_COMPRESSION)) {
8691 add_error("Unable to add filament sequence file to archive");
8692 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__
8693 << boost::format(", store filament sequence to 3mf, length %1%, failed\n") % out.length();
8694 return false;
8695 }
8696
8697 return true;
8698 }
const std::string FILAMENT_SEQUENCE_FILE
Definition bbs_3mf.cpp:234

References _add_filament_sequence_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::PlateData::filament_change_sequence, FILAMENT_SEQUENCE_FILE, Slic3r::PlateData::nozzle_change_sequence, Slic3r::PlateData::nozzle_group_result, and Slic3r::PlateData::optimal_assignment.

Referenced by _add_filament_sequence_file_to_archive(), _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_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_file_to_archive ( mz_zip_archive & archive,
const std::string & path_in_zip,
const std::string & file_path )
private
6913 {
6914 static std::string const nocomp_exts[] = {".png", ".jpg", ".mp4", ".jpeg", ".zip", ".3mf"};
6915 auto end = nocomp_exts + sizeof(nocomp_exts) / sizeof(nocomp_exts[0]);
6916 bool nocomp = std::find_if(nocomp_exts, end, [&path_in_zip](auto & ext) { return boost::algorithm::ends_with(path_in_zip, ext); }) != end;
6917#if WRITE_ZIP_LANGUAGE_ENCODING
6918 bool result = mz_zip_writer_add_file(&archive, path_in_zip.c_str(), encode_path(src_file_path.c_str()).c_str(), NULL, 0, nocomp ? MZ_NO_COMPRESSION : MZ_DEFAULT_LEVEL);
6919#else
6920 std::string native_path = encode_path(path_in_zip.c_str());
6921 std::string extra = ZipUnicodePathExtraField::encode(path_in_zip, native_path);
6922 bool result = mz_zip_writer_add_file_ex(&archive, native_path.c_str(), encode_path(src_file_path.c_str()).c_str(), NULL, 0, nocomp ? MZ_ZIP_FLAG_ASCII_FILENAME : MZ_DEFAULT_COMPRESSION,
6923 extra.c_str(), extra.length(), extra.c_str(), extra.length());
6924#endif
6925 if (!result) {
6926 add_error("Unable to add file " + src_file_path + " to archive");
6927 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", Unable to add file %1% to archive %2%\n") % src_file_path % path_in_zip;
6928 } else {
6929 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add file %1% to archive %2%\n") % src_file_path % path_in_zip;
6930 }
6931 return result;
6932 }
std::string encode_path(const char *src)
Definition utils.cpp:1382
static std::string encode(std::string const &u8path, std::string const &path)
Definition bbs_3mf.cpp:131

References _add_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), ZipUnicodePathExtraField::encode(), and Slic3r::encode_path().

Referenced by _add_auxiliary_dir_to_archive(), _add_file_to_archive(), _add_project_config_file_to_archive(), _add_project_embedded_presets_to_archive(), and _save_model_to_file().

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

◆ _add_gcode_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_gcode_file_to_archive ( mz_zip_archive & archive,
const Model & model,
PlateDataPtrs & plate_data_list,
Export3mfProgressFn proFn = nullptr )
private
9006{
9007 bool result = true;
9008 bool cb_cancel = false;
9009
9010 PlateDataPtrs plate_data_list2;
9011 for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
9012 {
9013 if (proFn) {
9014 proFn(EXPORT_STAGE_ADD_GCODE, i, plate_data_list.size(), cb_cancel);
9015 if (cb_cancel)
9016 return false;
9017 }
9018
9019 PlateData* plate_data = plate_data_list[i];
9020 if (!plate_data->gcode_file.empty() && plate_data->is_sliced_valid && boost::filesystem::exists(plate_data->gcode_file)) {
9021 plate_data_list2.push_back(plate_data);
9022 }
9023 }
9024
9025 boost::mutex mutex;
9026 tbb::parallel_for(tbb::blocked_range<size_t>(0, plate_data_list2.size(), 1), [this, &plate_data_list2, &root_archive = archive, &mutex, &result](const tbb::blocked_range<size_t>& range) {
9027 for (int i = range.begin(); i < range.end(); ++i) {
9028 PlateData* plate_data = plate_data_list2[i];
9029 auto src_gcode_file = plate_data->gcode_file;
9030 std::string gcode_in_3mf = (boost::format(GCODE_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
9031
9032 plate_data->gcode_file = gcode_in_3mf;
9033 mz_zip_archive archive;
9034 mz_zip_writer_staged_context context;
9035 mz_zip_zero_struct(&archive);
9036 mz_zip_writer_init_heap(&archive, 0, 1024 * 1024);
9037 {
9038 mz_zip_writer_add_staged_open(&archive, &context, gcode_in_3mf.c_str(), m_zip64 ? (uint64_t(1) << 30) * 16 : (uint64_t(1) << 32) - 1, nullptr, nullptr, 0,
9039 MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0);
9040 boost::filesystem::path src_gcode_path(src_gcode_file);
9041 if (!boost::filesystem::exists(src_gcode_path)) {
9042 BOOST_LOG_TRIVIAL(error) << "Gcode is missing, filename = " << src_gcode_file;
9043 result = false;
9044 }
9045 boost::filesystem::ifstream ifs(src_gcode_file, std::ios::binary);
9046 std::string buf(64 * 1024, 0);
9047 while (ifs) {
9048 ifs.read(buf.data(), buf.size());
9049 mz_zip_writer_add_staged_data(&context, buf.data(), ifs.gcount());
9050 }
9051 mz_zip_writer_add_staged_finish(&context);
9052 }
9053 void *ppBuf; size_t pSize;
9054 mz_zip_writer_finalize_heap_archive(&archive, &ppBuf, &pSize);
9055 mz_zip_writer_end(&archive);
9056 mz_zip_zero_struct(&archive);
9057 mz_zip_reader_init_mem(&archive, ppBuf, pSize, 0);
9058 {
9059 boost::unique_lock l(mutex);
9060 mz_zip_writer_add_from_zip_reader(&root_archive, &archive, 0);
9061 }
9062 mz_zip_reader_end(&archive);
9063 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", store %1% to 3mf %2%\n") % src_gcode_file % gcode_in_3mf;
9064 }
9065 });
9066 for (const PlateData *plate_data : plate_data_list2) {
9067 if (plate_data->statistics.empty())
9068 continue;
9069 const std::string statistics_in_3mf = (boost::format(STATISTICS_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
9070 if (!mz_zip_writer_add_mem(&archive, statistics_in_3mf.c_str(), plate_data->statistics.data(), plate_data->statistics.size(),
9071 MZ_DEFAULT_COMPRESSION)) {
9072 add_error("Unable to add statistics file to archive");
9073 result = false;
9074 }
9075 }
9076 return result;
9077}
#define STATISTICS_FILE_FORMAT
Definition bbs_3mf.hpp:35
boost::mutex mutex
Definition bbs_3mf.cpp:812
std::vector< PlateData * > PlateDataPtrs
Definition bbs_3mf.hpp:278
auto range(Cont &&cont)
Definition libslic3r.h:405
const int EXPORT_STAGE_ADD_GCODE
Definition bbs_3mf.hpp:255

References _add_gcode_file_to_archive(), Slic3r::EXPORT_STAGE_ADD_GCODE, Slic3r::PlateData::gcode_file, Slic3r::PlateData::is_sliced_valid, Slic3r::_BBS_3MF_Base::mutex, and Slic3r::range().

Referenced by _add_gcode_file_to_archive(), _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_layer_config_ranges_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_layer_config_ranges_file_to_archive ( mz_zip_archive & archive,
Model & model )
private
7995 {
7996 std::string out = "";
7997 pt::ptree tree;
7998
7999 unsigned int object_cnt = 0;
8000 for (const ModelObject* object : model.objects) {
8001 object_cnt++;
8002 const t_layer_config_ranges& ranges = object->layer_config_ranges;
8003 if (!ranges.empty())
8004 {
8005 pt::ptree& obj_tree = tree.add("objects.object","");
8006
8007 obj_tree.put("<xmlattr>.id", object_cnt);
8008
8009 // Store the layer config ranges.
8010 for (const auto& range : ranges) {
8011 pt::ptree& range_tree = obj_tree.add("range", "");
8012
8013 // store minX and maxZ
8014 range_tree.put("<xmlattr>.min_z", range.first.first);
8015 range_tree.put("<xmlattr>.max_z", range.first.second);
8016
8017 // store range configuration
8018 const ModelConfig& config = range.second;
8019 for (const std::string& opt_key : config.keys()) {
8020 pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
8021 opt_tree.put("<xmlattr>.opt_key", opt_key);
8022 }
8023 }
8024 }
8025 }
8026
8027 if (!tree.empty()) {
8028 std::ostringstream oss;
8029 pt::write_xml(oss, tree);
8030 out = oss.str();
8031
8032 // Post processing("beautification") of the output string for a better preview
8033 boost::replace_all(out, "><object", ">\n <object");
8034 boost::replace_all(out, "><range", ">\n <range");
8035 boost::replace_all(out, "><option", ">\n <option");
8036 boost::replace_all(out, "></range>", ">\n </range>");
8037 boost::replace_all(out, "></object>", ">\n </object>");
8038 // OR just
8039 boost::replace_all(out, "><", ">\n<");
8040 }
8041
8042 if (!out.empty()) {
8043 if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
8044 add_error("Unable to add layer heights profile file to archive");
8045 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add layer heights profile file to archive\n");
8046 return false;
8047 }
8048 }
8049
8050 return true;
8051 }
const std::string LAYER_CONFIG_RANGES_FILE
Definition 3mf.cpp:104
std::map< t_layer_height_range, ModelConfig > t_layer_config_ranges
Definition Slicing.hpp:160

References _add_layer_config_ranges_file_to_archive(), Slic3r::_BBS_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::_BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive ( mz_zip_archive & archive,
Model & model )
private
7947 {
7949 std::string out = "";
7950 char buffer[1024];
7951
7952 unsigned int count = 0;
7953 for (const ModelObject* object : model.objects) {
7954 ++count;
7955 const std::vector<double>& layer_height_profile = object->layer_height_profile.get();
7956 if (layer_height_profile.size() >= 4 && layer_height_profile.size() % 2 == 0) {
7957 snprintf(buffer, 1024, "object_id=%d|", count);
7958 out += buffer;
7959
7960 // Store the layer height profile as a single semicolon separated list.
7961 for (size_t i = 0; i < layer_height_profile.size(); ++i) {
7962 snprintf(buffer, 1024, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
7963 out += buffer;
7964 }
7965
7966 out += "\n";
7967 }
7968 }
7969
7970 if (!out.empty()) {
7971 if (!mz_zip_writer_add_mem(&archive, BBS_LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
7972 add_error("Unable to add layer heights profile file to archive");
7973 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add layer heights profile file to archive\n");
7974 return false;
7975 }
7976 }
7977
7978 return true;
7979 }
const std::string BBS_LAYER_HEIGHTS_PROFILE_FILE
Definition bbs_3mf.cpp:235
bool is_decimal_separator_point()
Definition LocalesUtils.cpp:78

References _add_layer_height_profile_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), BBS_LAYER_HEIGHTS_PROFILE_FILE, count, Slic3r::is_decimal_separator_point(), 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::_BBS_3MF_Exporter::_add_mesh_to_object_stream ( std::function< bool(std::string &, bool)> const & flush,
ObjectData const & object_data ) const
private
7594 {
7595 std::string output_buffer;
7596
7597#if 0
7598 auto flush = [this, &output_buffer, &context](bool force = false) {
7599 if ((force && ! output_buffer.empty()) || output_buffer.size() >= 65536 * 16) {
7600 if (! mz_zip_writer_add_staged_data(&context, output_buffer.data(), output_buffer.size())) {
7601 add_error("Error during writing or compression");
7602 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Error during writing or compression\n");
7603 return false;
7604 }
7605 output_buffer.clear();
7606 }
7607 return true;
7608 };
7609#endif
7610
7611 /*output_buffer += " <";
7612 output_buffer += MESH_TAG;
7613 output_buffer += ">\n <";
7614 output_buffer += VERTICES_TAG;
7615 output_buffer += ">\n";*/
7616
7617 auto format_coordinate = [](float f, char *buf) -> char* {
7619#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
7620 // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
7621 // https://github.com/boostorg/spirit/pull/586
7622 // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
7623 // The code is left here for the ocasion boost guys improve.
7624 coordinate_type_fixed const coordinate_fixed = coordinate_type_fixed();
7625 coordinate_type_scientific const coordinate_scientific = coordinate_type_scientific();
7626 // Format "f" in a fixed format.
7627 char *ptr = buf;
7628 boost::spirit::karma::generate(ptr, coordinate_fixed, f);
7629 // Format "f" in a scientific format.
7630 char *ptr2 = ptr;
7631 boost::spirit::karma::generate(ptr2, coordinate_scientific, f);
7632 // Return end of the shorter string.
7633 auto len2 = ptr2 - ptr;
7634 if (ptr - buf > len2) {
7635 // Move the shorter scientific form to the front.
7636 memcpy(buf, ptr, len2);
7637 ptr = buf + len2;
7638 }
7639 // Return pointer to the end.
7640 return ptr;
7641#else
7642 // Round-trippable float, shortest possible.
7643 return buf + sprintf(buf, "%.9g", f);
7644#endif
7645 };
7646
7647 auto const & object = *object_data.object;
7648
7649 char buf[256];
7650 unsigned int vertices_count = 0;
7651 //unsigned int triangles_count = 0;
7652 for (unsigned int index = 0; index < object.volumes.size(); index++) {
7653 ModelVolume *volume = object.volumes[index];
7654 if (volume == nullptr)
7655 continue;
7656
7657 int volume_id = object_data.volumes_objectID.find(volume)->second;
7658 if (m_share_mesh && volume_id == 0)
7659 continue;
7660
7661 //if (!volume->mesh().stats().repaired())
7662 // throw Slic3r::FileIOError("store_3mf() requires repair()");
7663
7664 const indexed_triangle_set &its = volume->mesh().its;
7665 if (its.vertices.empty()) {
7666 add_error("Found invalid mesh");
7667 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Found invalid mesh\n");
7668 return false;
7669 }
7670
7671 // Orca#7574: always use "model" type to follow the 3MF Core Specification:
7672 // https://github.com/3MFConsortium/spec_core/blob/20c079eef39e45ed223b8443dc9f34cbe32dc2c2/3MF%20Core%20Specification.md#3431-item-element
7673 // > Note: items MUST NOT reference objects of type "other", either directly or recursively.
7674 // This won't break anything because when loading the file Orca (and Bambu) simply does not care about the actual object type at all (as long as it's one of "model" & "other");
7675 // But PrusaSlicer requires the type to be "model".
7676 std::string type = "model";
7677
7678 output_buffer += " <";
7679 output_buffer += OBJECT_TAG;
7680 output_buffer += " id=\"";
7681 output_buffer += std::to_string(volume_id);
7682 if (m_production_ext) {
7683 std::stringstream stream;
7684 reset_stream(stream);
7685 stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) (index + (object_data.backup_id << 16))} << SUB_OBJECT_UUID_SUFFIX;
7686 //output_buffer += "\" ";
7687 //output_buffer += PUUID_ATTR;
7688 //output_buffer += "=\"";
7689 //output_buffer += std::to_string(hex_wrap<boost::uint32_t>{(boost::uint32_t)backup_id});
7690 //output_buffer += OBJECT_UUID_SUFFIX;
7691 output_buffer += stream.str();
7692 }
7693 output_buffer += "\" type=\"";
7694 output_buffer += type;
7695 output_buffer += "\">\n";
7696 output_buffer += " <";
7697 output_buffer += MESH_TAG;
7698 output_buffer += ">\n <";
7699 output_buffer += VERTICES_TAG;
7700 output_buffer += ">\n";
7701
7702 vertices_count += (int)its.vertices.size();
7703
7704 for (size_t i = 0; i < its.vertices.size(); ++i) {
7705 //don't save the volume's matrix into vertex data
7706 //add the shared mesh logic
7707 //Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
7708 Vec3f v = its.vertices[i];
7709 char* ptr = buf;
7710 boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\"");
7711 ptr = format_coordinate(v.x(), ptr);
7712 boost::spirit::karma::generate(ptr, "\" y=\"");
7713 ptr = format_coordinate(v.y(), ptr);
7714 boost::spirit::karma::generate(ptr, "\" z=\"");
7715 ptr = format_coordinate(v.z(), ptr);
7716 boost::spirit::karma::generate(ptr, "\"/>\n");
7717 *ptr = '\0';
7718 output_buffer += buf;
7719 if (!flush(output_buffer, false))
7720 return false;
7721 }
7722 //}
7723
7724 output_buffer += " </";
7725 output_buffer += VERTICES_TAG;
7726 output_buffer += ">\n <";
7727 output_buffer += TRIANGLES_TAG;
7728 output_buffer += ">\n";
7729
7730 //for (ModelVolume* volume : object.volumes) {
7731 // if (volume == nullptr)
7732 // continue;
7733
7734 //BBS: as we stored matrix seperately, not multiplied into vertex
7735 //we don't need to consider this left hand case specially
7736 //bool is_left_handed = volume->is_left_handed();
7737 bool is_left_handed = false;
7738 //VolumeToOffsetsMap::iterator volume_it = volumes_objectID.find(volume);
7739 //assert(volume_it != volumes_objectID.end());
7740
7741 //const indexed_triangle_set &its = volume->mesh().its;
7742
7743 // updates triangle offsets
7744 //unsigned int first_triangle_id = triangles_count;
7745 //triangles_count += (int)its.indices.size();
7746 //unsigned int last_triangle_id = triangles_count - 1;
7747
7748 for (int i = 0; i < int(its.indices.size()); ++ i) {
7749 {
7750 const Vec3i32 &idx = its.indices[i];
7751 char *ptr = buf;
7752 boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
7753 " v1=\"" << boost::spirit::int_ <<
7754 "\" v2=\"" << boost::spirit::int_ <<
7755 "\" v3=\"" << boost::spirit::int_ << "\"",
7756 idx[is_left_handed ? 2 : 0],
7757 idx[1],
7758 idx[is_left_handed ? 0 : 2]);
7759 *ptr = '\0';
7760 output_buffer += buf;
7761 }
7762
7763 std::string custom_supports_data_string = volume->supported_facets.get_triangle_as_string(i);
7764 if (! custom_supports_data_string.empty()) {
7765 output_buffer += " ";
7766 output_buffer += CUSTOM_SUPPORTS_ATTR;
7767 output_buffer += "=\"";
7768 output_buffer += custom_supports_data_string;
7769 output_buffer += "\"";
7770 }
7771
7772 std::string custom_seam_data_string = volume->seam_facets.get_triangle_as_string(i);
7773 if (! custom_seam_data_string.empty()) {
7774 output_buffer += " ";
7775 output_buffer += CUSTOM_SEAM_ATTR;
7776 output_buffer += "=\"";
7777 output_buffer += custom_seam_data_string;
7778 output_buffer += "\"";
7779 }
7780
7781 std::string mmu_painting_data_string = volume->mmu_segmentation_facets.get_triangle_as_string(i);
7782 if (! mmu_painting_data_string.empty()) {
7783 output_buffer += " ";
7784 output_buffer += MMU_SEGMENTATION_ATTR;
7785 output_buffer += "=\"";
7786 output_buffer += mmu_painting_data_string;
7787 output_buffer += "\"";
7788 }
7789
7790 std::string fuzzy_skin_painting_data_string = volume->fuzzy_skin_facets.get_triangle_as_string(i);
7791 if (!fuzzy_skin_painting_data_string.empty()) {
7792 output_buffer += " ";
7793 output_buffer += CUSTOM_FUZZY_SKIN_ATTR;
7794 output_buffer += "=\"";
7795 output_buffer += fuzzy_skin_painting_data_string;
7796 output_buffer += "\"";
7797 }
7798
7799 // One attribute per texture displacement layer slot. Older readers ignore attributes they do
7800 // not know, so a project written here still opens in a build without the feature - it just
7801 // loses the paint, which is also all it could have done with it.
7802 for (int slot = 0; slot < TEXTURE_DISPLACEMENT_MAX_LAYERS; ++slot) {
7803 const std::string texture_paint = volume->texture_displacement_facet(slot).get_triangle_as_string(i);
7804 if (texture_paint.empty())
7805 continue;
7806 output_buffer += " ";
7807 output_buffer += TEXTURE_DISPLACEMENT_ATTRS[slot];
7808 output_buffer += "=\"";
7809 output_buffer += texture_paint;
7810 output_buffer += "\"";
7811 }
7812
7813 // BBS
7814 if (i < its.properties.size()) {
7815 std::string prop_str = its.properties[i].to_string();
7816 if (!prop_str.empty()) {
7817 output_buffer += " ";
7818 output_buffer += FACE_PROPERTY_ATTR;
7819 output_buffer += "=\"";
7820 output_buffer += prop_str;
7821 output_buffer += "\"";
7822 }
7823 }
7824
7825 output_buffer += "/>\n";
7826
7827 if (! flush(output_buffer, false))
7828 return false;
7829 }
7830 output_buffer += " </";
7831 output_buffer += TRIANGLES_TAG;
7832 output_buffer += ">\n </";
7833 output_buffer += MESH_TAG;
7834 output_buffer += ">\n";
7835 output_buffer += " </";
7836 output_buffer += OBJECT_TAG;
7837 output_buffer += ">\n";
7838 }
7839
7840 // Force flush.
7841 return flush(output_buffer, true);
7842 }
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 * 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
static constexpr const char * OBJECT_TAG
Definition 3mf.cpp:111
static constexpr const char * TEXTURE_DISPLACEMENT_ATTRS[Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS]
Definition bbs_3mf.cpp:361
static constexpr const char * CUSTOM_FUZZY_SKIN_ATTR
Definition bbs_3mf.cpp:356
static constexpr const char * SUB_OBJECT_UUID_SUFFIX
Definition bbs_3mf.cpp:330
static constexpr const char * FACE_PROPERTY_ATTR
Definition bbs_3mf.cpp:367
bool m_share_mesh
Definition bbs_3mf.cpp:6231
static constexpr size_t TEXTURE_DISPLACEMENT_MAX_LAYERS
Definition TextureDisplacement.hpp:44
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65
static void reset_stream(std::stringstream &stream)
Definition 3mf.cpp:2583
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

References _add_mesh_to_object_stream(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::_BBS_3MF_Exporter::ObjectData::backup_id, CUSTOM_FUZZY_SKIN_ATTR, CUSTOM_SEAM_ATTR, CUSTOM_SUPPORTS_ATTR, Slic3r::f(), FACE_PROPERTY_ATTR, Slic3r::ModelVolume::fuzzy_skin_facets, Slic3r::FacetsAnnotation::get_triangle_as_string(), Slic3r::is_decimal_separator_point(), Slic3r::TriangleMesh::its, m_production_ext, m_share_mesh, Slic3r::ModelVolume::mesh(), MESH_TAG, MMU_SEGMENTATION_ATTR, Slic3r::ModelVolume::mmu_segmentation_facets, Slic3r::_BBS_3MF_Exporter::ObjectData::object, OBJECT_TAG, PUUID_ATTR, Slic3r::reset_stream(), Slic3r::ModelVolume::seam_facets, SUB_OBJECT_UUID_SUFFIX, Slic3r::ModelVolume::supported_facets, TEXTURE_DISPLACEMENT_ATTRS, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, TRIANGLE_TAG, TRIANGLES_TAG, VERTEX_TAG, VERTICES_TAG, and Slic3r::_BBS_3MF_Exporter::ObjectData::volumes_objectID.

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::_BBS_3MF_Exporter::_add_model_config_file_to_archive ( mz_zip_archive & archive,
const Model & model,
PlateDataPtrs & plate_data_list,
const ObjectToObjectDataMap & objects_data,
const DynamicPrintConfig & config,
int export_plate_idx = -1,
bool save_gcode = true,
bool use_loaded_id = false )
private
8297 {
8298 std::stringstream stream;
8299 // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
8300 // when loaded as accurately as possible.
8301 stream << std::setprecision(std::numeric_limits<double>::max_digits10);
8302 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
8303 stream << "<" << CONFIG_TAG << ">\n";
8304
8305 if (!m_skip_model)
8306 for (const ObjectToObjectDataMap::value_type& obj_metadata : objects_data) {
8307 auto object_data = obj_metadata.second;
8308 const ModelObject *obj = object_data.object;
8309 if (obj != nullptr) {
8310 // Output of instances count added because of github #3435, currently not used by PrusaSlicer
8311 //stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
8312 stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << object_data.object_id
8313 << "\" " << AUTO_DROP_ATTR << "=\"" << obj->auto_drop << "\">\n";
8314
8315 // stores object's name
8316 if (!obj->name.empty())
8317 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
8318
8319 //BBS: store object's module name
8320 if (!obj->module_name.empty())
8321 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"module\" " << VALUE_ATTR << "=\"" << xml_escape(obj->module_name) << "\"/>\n";
8322
8323 // stores object's config data
8324 for (const std::string& key : obj->config.keys()) {
8325 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
8326 }
8327
8328 for (const ModelVolume* volume : obj_metadata.second.object->volumes) {
8329 if (volume != nullptr) {
8330 const VolumeToObjectIDMap& objectIDs = obj_metadata.second.volumes_objectID;
8331 VolumeToObjectIDMap::const_iterator it = objectIDs.find(volume);
8332 if (it != objectIDs.end()) {
8333 // stores volume's offsets
8334 stream << " <" << PART_TAG << " ";
8335 //stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
8336 //stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\" ";
8337 int volume_id = it->second;
8338 if (m_share_mesh && volume_id == 0)
8339 volume_id = m_volume_paths.find(volume)->second.second;
8340 stream << ID_ATTR << "=\"" << volume_id << "\" ";
8341
8342 // Older slicers must recognize the base type even when they ignore seam metadata.
8343 const ModelVolumeType stored_type = volume->is_precise_seam() ? ModelVolumeType::PARAMETER_MODIFIER : volume->type();
8344 stream << SUBTYPE_ATTR << "=\"" << ModelVolume::type_to_string(stored_type) << "\">\n";
8345 if (volume->is_precise_seam())
8346 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRECISE_SEAM_TYPE_KEY << "\" " << VALUE_ATTR << "=\"" <<
8347 ModelVolume::type_to_string(volume->type()) << "\"/>\n";
8348 //stream << " <" << PART_TAG << " " << ID_ATTR << "=\"" << it->second << "\" " << SUBTYPE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\">\n";
8349
8350 // stores volume's name
8351 if (!volume->name.empty())
8352 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
8353 //stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
8354
8355 // stores volume's modifier field (legacy, to support old slicers)
8356 /*if (volume->is_modifier())
8357 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << PART_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
8358 // stores volume's type (overrides the modifier field above)
8359 stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << PART_TYPE << "\" " << KEY_ATTR << "=\"" << PART_TYPE_KEY << "\" " <<
8360 VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";*/
8361
8362 // stores volume's local matrix
8363 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
8364 Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
8365 for (int r = 0; r < 4; ++r) {
8366 for (int c = 0; c < 4; ++c) {
8367 stream << matrix(r, c);
8368 if (r != 3 || c != 3)
8369 stream << " ";
8370 }
8371 }
8372 stream << "\"/>\n";
8373
8374 // stores volume's source data
8375 {
8376 auto file_path =get_dealed_platform_path(volume->source.input_file);
8377 std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : file_path.filename().string());
8378 //std::string prefix = std::string(" <") + METADATA_TAG + " " + KEY_ATTR + "=\"";
8379 std::string prefix = std::string(" <") + METADATA_TAG + " " + KEY_ATTR + "=\"";
8380 if (! volume->source.input_file.empty()) {
8381 stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
8382 stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
8383 stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
8384 stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
8385 stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
8386 stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
8387 }
8388 assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
8389 if (volume->source.is_converted_from_inches)
8390 stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
8391 else if (volume->source.is_converted_from_meters)
8392 stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n";
8393 }
8394
8395 // stores volume's config data
8396 for (const std::string& key : volume->config.keys()) {
8397 // Seam settings are inactive but must survive changing the helper back into a part/modifier.
8398 const bool dormant = volume->is_precise_seam();
8399 const std::string stored_key = dormant ? PRECISE_SEAM_CONFIG_PREFIX + key : key;
8400 const std::string value = volume->config.opt_serialize(key);
8401 // Config serialization is C-style, not XML: escape active settings too, including tabs.
8402 stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << stored_key << "\" " << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(value) << "\"/>\n";
8403 }
8404
8405 if (const std::optional<EmbossShape> &es = volume->emboss_shape; es.has_value()) {
8406 to_xml(stream, *es, *volume, archive, m_fullpath_sources);
8407 }
8408
8409 add_texture_displacement(stream, *volume, archive, std::to_string(volume->id().id));
8410
8411 if (const std::optional<TextConfiguration> &tc = volume->text_configuration;
8412 tc.has_value())
8414
8415 // stores mesh's statistics
8416 const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
8417 stream << " <" << MESH_STAT_TAG << " ";
8418 stream << MESH_STAT_EDGES_FIXED << "=\"" << stats.edges_fixed << "\" ";
8419 stream << MESH_STAT_DEGENERATED_FACETS << "=\"" << stats.degenerate_facets << "\" ";
8420 stream << MESH_STAT_FACETS_REMOVED << "=\"" << stats.facets_removed << "\" ";
8421 stream << MESH_STAT_FACETS_RESERVED << "=\"" << stats.facets_reversed << "\" ";
8422 stream << MESH_STAT_BACKWARDS_EDGES << "=\"" << stats.backwards_edges << "\"/>\n";
8423
8424 stream << " </" << PART_TAG << ">\n";
8425 }
8426 }
8427 }
8428
8429 stream << " </" << OBJECT_TAG << ">\n";
8430 }
8431 }
8432
8433 //BBS: store plate related logic
8434 std::vector<std::string> gcode_paths;
8435 for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
8436 {
8437 PlateData* plate_data = plate_data_list[i];
8438 int instance_size = plate_data->objects_and_instances.size();
8439
8440 if (plate_data != nullptr) {
8441 stream << " <" << PLATE_TAG << ">\n";
8442 //plate index
8443 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PLATERID_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->plate_index + 1 << "\"/>\n";
8444 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PLATER_NAME_ATTR << "\" " << VALUE_ATTR << "=\"" << xml_escape(plate_data->plate_name.c_str()) << "\"/>\n";
8445 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << LOCK_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->locked<< "\"/>\n";
8446 ConfigOption* bed_type_opt = plate_data->config.option("curr_bed_type");
8448 if (bed_type_opt != nullptr && bed_type_names.size() > bed_type_opt->getInt())
8449 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << BED_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << bed_type_names[bed_type_opt->getInt()] << "\"/>\n";
8450
8451 ConfigOption* print_sequence_opt = plate_data->config.option("print_sequence");
8453 if (print_sequence_opt != nullptr && print_sequence_names.size() > print_sequence_opt->getInt())
8454 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"" << print_sequence_names[print_sequence_opt->getInt()] << "\"/>\n";
8455
8456 ConfigOptionInts *first_layer_print_sequence_opt = plate_data->config.option<ConfigOptionInts>("first_layer_print_sequence");
8457 if (first_layer_print_sequence_opt != nullptr) {
8458 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FIRST_LAYER_PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"";
8459 const std::vector<int>& values = first_layer_print_sequence_opt->values;
8460 for (int i = 0; i < values.size(); ++i) {
8461 stream << values[i];
8462 if (i != (values.size() - 1))
8463 stream << " ";
8464 }
8465 stream << "\"/>\n";
8466 }
8467
8468 ConfigOptionInts *other_layers_print_sequence_opt = plate_data->config.option<ConfigOptionInts>("other_layers_print_sequence");
8469 if (other_layers_print_sequence_opt != nullptr) {
8470 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OTHER_LAYERS_PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"";
8471 const std::vector<int> &values = other_layers_print_sequence_opt->values;
8472 for (int i = 0; i < values.size(); ++i) {
8473 stream << values[i];
8474 if (i != (values.size() - 1))
8475 stream << " ";
8476 }
8477 stream << "\"/>\n";
8478 }
8479
8480 const ConfigOptionInt *sequence_nums_opt = dynamic_cast<const ConfigOptionInt *>(plate_data->config.option("other_layers_print_sequence_nums"));
8481 if (sequence_nums_opt != nullptr) {
8482 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR << "\" " << VALUE_ATTR << "=\"" << sequence_nums_opt->getInt() << "\"/>\n";
8483 }
8484
8485 ConfigOption* spiral_mode_opt = plate_data->config.option("spiral_mode");
8486 if (spiral_mode_opt)
8487 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SPIRAL_VASE_MODE << "\" " << VALUE_ATTR << "=\"" << spiral_mode_opt->getBool() << "\"/>\n";
8488
8489 {
8490 // Mode names are user supplied free text, so the value has to be escaped for an
8491 // attribute. xml_escape_double_quotes_attribute_value() is used rather than
8492 // xml_escape() because it also emits tab/CR/LF as numeric character references:
8493 // XML normalizes literal whitespace in attribute values on read, which would
8494 // silently rename the mode. The reader takes the value straight from expat,
8495 // which resolves both entities and character references, so this round-trips.
8496 auto* imex_mode_opt = plate_data->config.option<ConfigOptionString>("imex_parallel_mode");
8497 if (imex_mode_opt && !imex_mode_opt->value.empty() && imex_mode_opt->value != kImexPrimaryMode)
8498 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IMEX_PARALLEL_MODE_ATTR << "\" " << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(imex_mode_opt->value) << "\"/>\n";
8499 }
8500 {
8501 auto* imex_hfm_opt = plate_data->config.option<ConfigOptionString>("imex_head_filament_map");
8502 if (imex_hfm_opt && !imex_hfm_opt->value.empty())
8503 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IMEX_HEAD_FILAMENT_MAP_ATTR << "\" " << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(imex_hfm_opt->value) << "\"/>\n";
8504 }
8505
8506 //filament map related
8507 ConfigOption* filament_map_mode_opt = plate_data->config.option("filament_map_mode");
8509 if (filament_map_mode_opt != nullptr && filament_map_mode_names.size() > filament_map_mode_opt->getInt())
8510 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FILAMENT_MAP_MODE_ATTR << "\" " << VALUE_ATTR << "=\"" << filament_map_mode_names[filament_map_mode_opt->getInt()] << "\"/>\n";
8511
8512 ConfigOptionInts* filament_maps_opt = plate_data->config.option<ConfigOptionInts>("filament_map");
8513 // filament map override global settings only when group mode overrides the global settings
8514 if (filament_map_mode_opt !=nullptr && filament_maps_opt != nullptr) {
8515 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FILAMENT_MAP_ATTR << "\" " << VALUE_ATTR << "=\"";
8516 const std::vector<int>& values = filament_maps_opt->values;
8517 for (int i = 0; i < values.size(); ++i) {
8518 stream << values[i];
8519 if (i != (values.size() - 1))
8520 stream << " ";
8521 }
8522 stream << "\"/>\n";
8523 }
8524
8525 ConfigOptionInts* filament_volume_maps_opt = plate_data->config.option<ConfigOptionInts>("filament_volume_map");
8526 if (filament_map_mode_opt != nullptr && filament_volume_maps_opt != nullptr) {
8527 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FILAMENT_VOL_MAP_ATTR << "\" " << VALUE_ATTR << "=\"";
8528 const std::vector<int>& volume_values = filament_volume_maps_opt->values;
8529 for (int i = 0; i < volume_values.size(); ++i) {
8530 stream << volume_values[i];
8531 if (i != (volume_values.size() - 1))
8532 stream << " ";
8533 }
8534 stream << "\"/>\n";
8535 }
8536
8537 if (save_gcode)
8538 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << GCODE_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << xml_escape(plate_data->gcode_file) << "\"/>\n";
8539 if (!plate_data->gcode_file.empty()) {
8540 gcode_paths.push_back(plate_data->gcode_file);
8541 }
8542 if (plate_data->plate_thumbnail.is_valid()) {
8543 std::string thumbnail_file_in_3mf = (boost::format(THUMBNAIL_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8544 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << thumbnail_file_in_3mf << "\"/>\n";
8545 }
8546 else if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))){
8547 std::string thumbnail_file_in_3mf = (boost::format(THUMBNAIL_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8548 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << thumbnail_file_in_3mf << "\"/>\n";
8549 }
8550
8551 if (!plate_data->no_light_thumbnail_file.empty()){
8552 std::string no_light_thumbnail_file_in_3mf = (boost::format(NO_LIGHT_THUMBNAIL_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8553 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NO_LIGHT_THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << no_light_thumbnail_file_in_3mf << "\"/>\n";
8554 }
8555
8556 if (!plate_data->top_file.empty()) {
8557 std::string top_file_in_3mf = (boost::format(TOP_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8558 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TOP_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << top_file_in_3mf << "\"/>\n";
8559 }
8560
8561 if (!plate_data->pick_file.empty()) {
8562 std::string pick_file_in_3mf = (boost::format(PICK_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8563 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PICK_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pick_file_in_3mf << "\"/>\n";
8564 }
8565
8566 /*if (!plate_data->pattern_file.empty()) {
8567 std::string pattern_file_in_3mf = (boost::format(PATTERN_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8568 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_file_in_3mf << "\"/>\n";
8569 }*/
8570 if (!plate_data->pattern_bbox_file.empty()) {
8571 std::string pattern_bbox_file_in_3mf = (boost::format(PATTERN_CONFIG_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
8572 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_BBOX_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_bbox_file_in_3mf << "\"/>\n";
8573 }
8574
8575 if (!m_skip_model && instance_size > 0)
8576 {
8577 for (unsigned int j = 0; j < instance_size; ++j)
8578 {
8579 stream << " <" << INSTANCE_TAG << ">\n";
8580 int obj_id = plate_data->objects_and_instances[j].first;
8581 int inst_id = plate_data->objects_and_instances[j].second;
8582 int identify_id = 0;
8583 ModelObject* obj = NULL;
8584 ModelInstance* inst = NULL;
8585 if (obj_id >= model.objects.size()) {
8586 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid object id %1%\n")%obj_id;
8587 }
8588 else
8589 obj = model.objects[obj_id];
8590
8591 if (obj && (inst_id >= obj->instances.size())) {
8592 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid instance id %1%\n")%inst_id;
8593 }
8594 else if (obj){
8595 inst = obj->instances[inst_id];
8596 if (use_loaded_id && (inst->loaded_id > 0))
8597 identify_id = inst->loaded_id;
8598 else
8599 identify_id = inst->id().id;
8600 }
8601 obj_id = objects_data.find(obj)->second.object_id;
8602
8603 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OBJECT_ID_ATTR << "\" " << VALUE_ATTR << "=\"" << obj_id << "\"/>\n";
8604 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << INSTANCEID_ATTR << "\" " << VALUE_ATTR << "=\"" << inst_id << "\"/>\n";
8605 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IDENTIFYID_ATTR << "\" " << VALUE_ATTR << "=\"" << identify_id << "\"/>\n";
8606 stream << " </" << INSTANCE_TAG << ">\n";
8607 }
8608 }
8609 stream << " </" << PLATE_TAG << ">\n";
8610 }
8611 }
8612
8613 // write model rels
8614 if (save_gcode)
8615 _add_relationships_file_to_archive(archive, BBS_MODEL_CONFIG_RELS_FILE, gcode_paths, {"http://schemas.bambulab.com/package/2021/gcode"}, Slic3r::PackingTemporaryData(), export_plate_idx);
8616
8617 if (!m_skip_model) {
8618 //BBS: store assemble related info
8619 stream << " <" << ASSEMBLE_TAG << ">\n";
8620 for (const ObjectToObjectDataMap::value_type& obj_metadata : objects_data) {
8621 auto object_data = obj_metadata.second;
8622 const ModelObject* obj = object_data.object;
8623 if (obj != nullptr) {
8624 for (int instance_idx = 0; instance_idx < obj->instances.size(); ++instance_idx) {
8625 if (obj->instances[instance_idx]->is_assemble_initialized()) {
8626 stream << " <" << ASSEMBLE_ITEM_TAG << " " << OBJECT_ID_ATTR << "=\"" << object_data.object_id << "\" ";
8627 stream << INSTANCEID_ATTR << "=\"" << instance_idx << "\" " << TRANSFORM_ATTR << "=\"";
8628 for (unsigned c = 0; c < 4; ++c) {
8629 for (unsigned r = 0; r < 3; ++r) {
8630 const Transform3d assemble_trans = obj->instances[instance_idx]->get_assemble_transformation().get_matrix();
8631 stream << assemble_trans(r, c);
8632 if (r != 2 || c != 3)
8633 stream << " ";
8634 }
8635 }
8636
8637 stream << "\" ";
8638
8639 stream << OFFSET_ATTR << "=\"";
8640 const Vec3d ofs2ass = obj->instances[instance_idx]->get_offset_to_assembly();
8641 stream << ofs2ass(0) << " " << ofs2ass(1) << " " << ofs2ass(2);
8642 stream << "\" />\n";
8643 }
8644 }
8645 }
8646 }
8647 stream << " </" << ASSEMBLE_TAG << ">\n";
8648 }
8649
8650 stream << "</" << CONFIG_TAG << ">\n";
8651
8652 std::string out = stream.str();
8653 if (!mz_zip_writer_add_mem(&archive, BBS_MODEL_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
8654 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add model config file to archive\n");
8655 add_error("Unable to add model config file to archive");
8656 return false;
8657 }
8658
8659 return true;
8660 }
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 * 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 * 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 * MESH_STAT_FACETS_REMOVED
Definition 3mf.cpp:172
static constexpr const char * VALUE_ATTR
Definition 3mf.cpp:146
static constexpr const char * SOURCE_OFFSET_Y_KEY
Definition 3mf.cpp:165
static constexpr const char * MATRIX_KEY
Definition 3mf.cpp:160
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 * 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 * MESH_STAT_DEGENERATED_FACETS
Definition 3mf.cpp:171
static constexpr const char * PATTERN_BBOX_FILE_ATTR
Definition bbs_3mf.cpp:397
static constexpr const char * PLATERID_ATTR
Definition bbs_3mf.cpp:401
static constexpr const char * MESH_STAT_TAG
Definition bbs_3mf.cpp:262
static constexpr const char * FILAMENT_MAP_ATTR
Definition bbs_3mf.cpp:388
static constexpr const char * IMEX_HEAD_FILAMENT_MAP_ATTR
Definition bbs_3mf.cpp:386
static constexpr const char * PICK_FILE_ATTR
Definition bbs_3mf.cpp:395
static constexpr const char * THUMBNAIL_FILE_ATTR
Definition bbs_3mf.cpp:392
static constexpr const char * GCODE_FILE_ATTR
Definition bbs_3mf.cpp:391
static constexpr const char * PLATER_NAME_ATTR
Definition bbs_3mf.cpp:402
const std::string BBS_MODEL_CONFIG_FILE
Definition bbs_3mf.cpp:231
static constexpr const char * OBJECT_ID_ATTR
Definition bbs_3mf.cpp:398
static constexpr const char * PART_TAG
Definition bbs_3mf.cpp:295
static constexpr const char * INSTANCE_TAG
Definition bbs_3mf.cpp:297
static constexpr const char * PRINT_SEQUENCE_ATTR
Definition bbs_3mf.cpp:376
static constexpr const char * FIRST_LAYER_PRINT_SEQUENCE_ATTR
Definition bbs_3mf.cpp:377
static constexpr const char * TOP_FILE_ATTR
Definition bbs_3mf.cpp:394
static constexpr const char * SUBTYPE_ATTR
Definition bbs_3mf.cpp:373
static constexpr const char * SPIRAL_VASE_MODE
Definition bbs_3mf.cpp:380
static constexpr const char * ASSEMBLE_ITEM_TAG
Definition bbs_3mf.cpp:300
static constexpr const char * PLATE_TAG
Definition bbs_3mf.cpp:296
static constexpr const char * OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR
Definition bbs_3mf.cpp:379
static constexpr const char * INSTANCEID_ATTR
Definition bbs_3mf.cpp:399
static constexpr const char * NO_LIGHT_THUMBNAIL_FILE_ATTR
Definition bbs_3mf.cpp:393
static constexpr const char * FILAMENT_MAP_MODE_ATTR
Definition bbs_3mf.cpp:387
static constexpr const char * BED_TYPE_ATTR
Definition bbs_3mf.cpp:375
static constexpr const char * IMEX_PARALLEL_MODE_ATTR
Definition bbs_3mf.cpp:381
static constexpr const char * AUTO_DROP_ATTR
Definition bbs_3mf.cpp:353
static constexpr const char * OTHER_LAYERS_PRINT_SEQUENCE_ATTR
Definition bbs_3mf.cpp:378
static constexpr const char * OFFSET_ATTR
Definition bbs_3mf.cpp:351
static constexpr const char * FILAMENT_VOL_MAP_ATTR
Definition bbs_3mf.cpp:389
static constexpr const char * IDENTIFYID_ATTR
Definition bbs_3mf.cpp:400
const std::string BBS_MODEL_CONFIG_RELS_FILE
Definition bbs_3mf.cpp:232
static constexpr const char * ASSEMBLE_TAG
Definition bbs_3mf.cpp:299
static constexpr const char * LOCK_ATTR
Definition bbs_3mf.cpp:374
#define THUMBNAIL_FILE_FORMAT
Definition bbs_3mf.hpp:36
#define PICK_FILE_FORMAT
Definition bbs_3mf.hpp:39
#define TOP_FILE_FORMAT
Definition bbs_3mf.hpp:38
#define NO_LIGHT_THUMBNAIL_FILE_FORMAT
Definition bbs_3mf.hpp:37
static const t_config_enum_names & get_enum_names()
static std::string type_to_string(const ModelVolumeType t)
Definition Model.cpp:2942
std::map< const ModelVolume *, int > VolumeToObjectIDMap
Definition bbs_3mf.cpp:6205
bool _add_relationships_file_to_archive(mz_zip_archive &archive, std::string const &from={}, std::vector< std::string > const &targets={}, std::vector< std::string > const &types={}, PackingTemporaryData data=PackingTemporaryData(), int export_plate_idx=-1, bool plate_thumbnail_written=false, bool plate_small_thumbnail_written=false) const
Definition bbs_3mf.cpp:7062
std::map< ModelVolume const *, std::pair< std::string, int > > m_volume_paths
Definition bbs_3mf.cpp:6235
bool m_skip_model
Definition bbs_3mf.cpp:6227
bool m_fullpath_sources
Definition bbs_3mf.cpp:6220
static void add_texture_displacement(std::stringstream &stream, const ModelVolume &volume, mz_zip_archive &archive, const std::string &id)
Definition bbs_3mf.cpp:8266
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96
ModelVolumeType
Definition Model.hpp:355
@ PARAMETER_MODIFIER
Definition Model.hpp:359
std::vector< std::string > t_config_enum_names
Definition Config.hpp:2070
boost::filesystem::path get_dealed_platform_path(std::string path_str)
Definition bbs_3mf.cpp:8249
ConfigOptionIntsTempl< false > ConfigOptionInts
Definition Config.hpp:1154
constexpr const char * kImexPrimaryMode
Definition IMEXHelpers.hpp:27
std::string xml_escape_double_quotes_attribute_value(std::string text)
Definition utils.cpp:1616
zaa_minimize_perimeter_height ConfigOptionString
Definition PrintConfig.hpp:1702
static void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume &volume, mz_zip_archive &archive, bool export_full_path)
Definition bbs_3mf.cpp:10134
static void to_xml(std::stringstream &stream, const TextConfiguration &tc)
Definition bbs_3mf.cpp:9919

References _add_model_config_file_to_archive(), _add_relationships_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::add_texture_displacement(), ASSEMBLE_ITEM_TAG, ASSEMBLE_TAG, Slic3r::ModelObject::auto_drop, AUTO_DROP_ATTR, Slic3r::RepairedMeshErrors::backwards_edges, BBS_MODEL_CONFIG_FILE, BBS_MODEL_CONFIG_RELS_FILE, BED_TYPE_ATTR, Slic3r::ModelObject::config, Slic3r::ModelVolume::config, Slic3r::PlateData::config, CONFIG_TAG, Slic3r::RepairedMeshErrors::degenerate_facets, Slic3r::RepairedMeshErrors::edges_fixed, Slic3r::ModelVolume::emboss_shape, Slic3r::RepairedMeshErrors::facets_removed, Slic3r::RepairedMeshErrors::facets_reversed, FILAMENT_MAP_ATTR, FILAMENT_MAP_MODE_ATTR, FILAMENT_VOL_MAP_ATTR, FIRST_LAYER_PRINT_SEQUENCE_ATTR, Slic3r::PlateData::gcode_file, GCODE_FILE_ATTR, Slic3r::get_dealed_platform_path(), Slic3r::ConfigOptionEnum< T >::get_enum_names(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelVolume::get_matrix(), Slic3r::ConfigOption::getBool(), Slic3r::ConfigOption::getInt(), Slic3r::ConfigOptionInt::getInt(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, ID_ATTR, IDENTIFYID_ATTR, IMEX_HEAD_FILAMENT_MAP_ATTR, IMEX_PARALLEL_MODE_ATTR, Slic3r::ModelVolume::Source::input_file, INSTANCE_TAG, INSTANCEID_ATTR, Slic3r::ModelObject::instances, Slic3r::ModelVolume::Source::is_converted_from_inches, Slic3r::ModelVolume::Source::is_converted_from_meters, Slic3r::ModelVolume::is_precise_seam(), Slic3r::ThumbnailData::is_valid(), KEY_ATTR, Slic3r::ModelConfig::keys(), Slic3r::kImexPrimaryMode, Slic3r::ModelInstance::loaded_id, LOCK_ATTR, Slic3r::PlateData::locked, m_fullpath_sources, m_share_mesh, m_skip_model, m_volume_paths, 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_STAT_TAG, METADATA_TAG, Slic3r::ModelObject::module_name, Slic3r::ModelObject::name, Slic3r::ModelVolume::name, NAME_KEY, Slic3r::PlateData::no_light_thumbnail_file, NO_LIGHT_THUMBNAIL_FILE_ATTR, NO_LIGHT_THUMBNAIL_FILE_FORMAT, Slic3r::ModelVolume::object, OBJECT_ID_ATTR, Slic3r::ModelVolume::Source::object_idx, OBJECT_TAG, Slic3r::Model::objects, Slic3r::PlateData::objects_and_instances, OFFSET_ATTR, Slic3r::ModelConfig::opt_serialize(), Slic3r::ConfigBase::option(), OTHER_LAYERS_PRINT_SEQUENCE_ATTR, OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR, Slic3r::PARAMETER_MODIFIER, PART_TAG, Slic3r::PlateData::pattern_bbox_file, PATTERN_BBOX_FILE_ATTR, PATTERN_CONFIG_FILE_FORMAT, Slic3r::PlateData::pick_file, PICK_FILE_ATTR, PICK_FILE_FORMAT, Slic3r::PlateData::plate_index, Slic3r::PlateData::plate_name, PLATE_TAG, Slic3r::PlateData::plate_thumbnail, PLATER_NAME_ATTR, PLATERID_ATTR, PRECISE_SEAM_CONFIG_PREFIX, PRECISE_SEAM_TYPE_KEY, PRINT_SEQUENCE_ATTR, 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, SPIRAL_VASE_MODE, Slic3r::TriangleMesh::stats(), SUBTYPE_ATTR, Slic3r::ModelVolume::text_configuration, Slic3r::PlateData::thumbnail_file, THUMBNAIL_FILE_ATTR, THUMBNAIL_FILE_FORMAT, Slic3r::TextConfigurationSerialization::to_xml(), Slic3r::to_xml(), Slic3r::PlateData::top_file, TOP_FILE_ATTR, TOP_FILE_FORMAT, Slic3r::ModelVolume::Source::transform, TRANSFORM_ATTR, Slic3r::ModelVolume::type(), Slic3r::ModelVolume::type_to_string(), VALUE_ATTR, Slic3r::ConfigOptionVector< int >::values, Slic3r::ModelVolume::Source::volume_idx, Slic3r::ModelObject::volumes, Slic3r::xml_escape(), and Slic3r::xml_escape_double_quotes_attribute_value().

Referenced by _add_model_config_file_to_archive(), _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_model_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_model_file_to_archive ( const std::string & filename,
mz_zip_archive & archive,
const Model & model,
ObjectToObjectDataMap & objects_data,
Export3mfProgressFn proFn = nullptr,
BBLProject * project = nullptr ) const
private
7160 {
7161 bool sub_model = !objects_data.empty();
7162 bool write_object = sub_model || !m_split_model;
7163
7164 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", filename %1%, m_split_model %2%, sub_model %3%")%filename % m_split_model % sub_model;
7165
7166#if WRITE_ZIP_LANGUAGE_ENCODING
7167 auto & zip_filename = filename;
7168#else
7169 std::string zip_filename = encode_path(filename.c_str());
7170 std::string extra = sub_model ? ZipUnicodePathExtraField::encode(filename, zip_filename) : "";
7171#endif
7172 mz_zip_writer_staged_context context;
7173 if (!mz_zip_writer_add_staged_open(&archive, &context, sub_model ? zip_filename.c_str() : MODEL_FILE.c_str(),
7174 m_zip64 ?
7175 // Maximum expected and allowed 3MF file size is 16GiB.
7176 // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
7177 (uint64_t(1) << 30) * 16 :
7178 // Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
7179 // GH issue #6193.
7180 (uint64_t(1) << 32) - 1,
7182 nullptr, nullptr, 0, MZ_DEFAULT_LEVEL, nullptr, 0, nullptr, 0)) {
7183#else
7184 nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, extra.c_str(), extra.length(), extra.c_str(), extra.length())) {
7185#endif
7186 add_error("Unable to add model file to archive");
7187 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add model file to archive\n");
7188 return false;
7189 }
7190
7191
7192 {
7193 std::stringstream stream;
7194 reset_stream(stream);
7195 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
7196 stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:BambuStudio=\"http://schemas.bambulab.com/package/2021\"";
7197 if (m_production_ext)
7198 stream << " xmlns:p=\"http://schemas.microsoft.com/3dmanufacturing/production/2015/06\" requiredextensions=\"p\"";
7199 stream << ">\n";
7200
7201 std::string origin;
7202 std::string name;
7203 std::string user_name;
7204 std::string user_id;
7205 std::string design_cover;
7206 std::string license;
7207 std::string description;
7208 std::string copyright;
7209 std::string rating;
7210 std::string model_id;
7211 std::string region_code;
7212 if (model.design_info) {
7213 user_name = model.design_info->Designer;
7214 user_id = model.design_info->DesignerUserId;
7215 BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer = " << user_name;
7216 BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer_user_id = " << user_id;
7217 }
7218
7219 if (project) {
7220 model_id = project->project_model_id;
7221 region_code = project->project_country_code;
7222 }
7223
7224 if (model.model_info) {
7225 design_cover = model.model_info->cover_file;
7226 license = model.model_info->license;
7227 description = model.model_info->description;
7228 copyright = model.model_info->copyright;
7229 name = model.model_info->model_name;
7230 origin = model.model_info->origin;
7231 BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer_cover = " << design_cover;
7232 }
7233 // remember to use metadata_item_map to store metadata info
7234 std::map<std::string, std::string> metadata_item_map;
7235 if (!sub_model) {
7236 // update metadat_items
7237 if (model.model_info && model.model_info.get()) {
7238 metadata_item_map = model.model_info.get()->metadata_items;
7239 }
7240
7241 metadata_item_map[BBL_MODEL_NAME_TAG] = xml_escape(name);
7242 metadata_item_map[BBL_ORIGIN_TAG] = xml_escape(origin);
7243 metadata_item_map[BBL_DESIGNER_TAG] = xml_escape(user_name);
7244 metadata_item_map[BBL_DESIGNER_USER_ID_TAG] = ""; // Orca: PRIVACY: do not store BBL user id in 3mf
7245 metadata_item_map[BBL_DESIGNER_COVER_FILE_TAG] = xml_escape(design_cover);
7246 metadata_item_map[BBL_DESCRIPTION_TAG] = xml_escape(description);
7247 metadata_item_map[BBL_COPYRIGHT_NORMATIVE_TAG] = xml_escape(copyright);
7248 metadata_item_map[BBL_LICENSE_TAG] = xml_escape(license);
7249
7250 /* save model info */
7251 if (!model_id.empty()) {
7252 metadata_item_map[BBL_MODEL_ID_TAG] = model_id;
7253 metadata_item_map[BBL_REGION_TAG] = region_code;
7254 }
7255
7256 // Orca: PRIVACY: do not store creation & modification date in 3mf
7257 metadata_item_map[BBL_CREATION_DATE_TAG] = "";
7258 metadata_item_map[BBL_MODIFICATION_TAG] = "";
7259 // Orca: Write the BambuStudio compatibility version string using SLIC3R_VERSION.
7260 // A minimal published 3MF writes no slicer tags at all: any tag would route old
7261 // receivers onto a geometry-only fallback whose baked-in popup misreports the
7262 // file ("old OrcaSlicer version" / "BambuStudio"), while tag-less files classify
7263 // as From_Other and import the geometry silently.
7264 if (m_minimal_published) {
7265 // metadata_item_map is seeded from the input file's metadata_items above, so a
7266 // project opened from a regular Orca/BBS 3MF still carries the slicer-identifying
7267 // tags it came with. Erase every one of them - not just the two most common -
7268 // so a published 3MF is fully tag-less: old receivers classify it as From_Other
7269 // and import the geometry silently instead of showing a baked-in "old version"
7270 // popup, and no version marker survives to seed a later re-save.
7271 metadata_item_map.erase(BBL_APPLICATION_TAG);
7272 metadata_item_map.erase(ORCASLICER_TAG);
7273 metadata_item_map.erase(BBS_3MF_VERSION);
7274 metadata_item_map.erase(BBS_3MF_VERSION1);
7275 } else {
7276 metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SLIC3R_VERSION).str();
7277 }
7278 }
7279 // The Bambu 3MF version marker is part of the slicer identity: omit it for a minimal
7280 // published file along with the tags erased above (skipping the overwrite alone would
7281 // leave the value the source file seeded into metadata_item_map).
7283 metadata_item_map[BBS_3MF_VERSION] = std::to_string(VERSION_BBS_3MF);
7284
7285 if (!model.mk_name.empty()) {
7286 metadata_item_map[BBL_MAKERLAB_TAG] = xml_escape(model.mk_name);
7287 BOOST_LOG_TRIVIAL(info) << "saved mk_name " << model.mk_name;
7288 }
7289 if (!model.mk_version.empty()) {
7290 metadata_item_map[BBL_MAKERLAB_VERSION_TAG] = xml_escape(model.mk_version);
7291 BOOST_LOG_TRIVIAL(info) << "saved mk_version " << model.mk_version;
7292 }
7293 if (!model.md_name.empty()) {
7294 for (unsigned int i = 0; i < model.md_name.size(); i++)
7295 {
7296 BOOST_LOG_TRIVIAL(info) << boost::format("saved metadata_name %1%, metadata_value %2%") %model.md_name[i] %model.md_value[i];
7297 metadata_item_map[model.md_name[i]] = xml_escape(model.md_value[i]);
7298 }
7299 }
7300
7301 // store metadata info
7302 for (auto item : metadata_item_map) {
7303 BOOST_LOG_TRIVIAL(info) << "bbs_3mf: save key= " << item.first << ", value = " << item.second;
7304 stream << " <" << METADATA_TAG << " name=\"" << item.first << "\">"
7305 << xml_escape(item.second) << "</" << METADATA_TAG << ">\n";
7306 if (item.first == BBL_APPLICATION_TAG && !m_minimal_published) {
7307 // The OrcaSlicer tag is only written for files that carry the Application
7308 // tag, which a minimal published 3MF erases (see the map assignment above):
7309 // the explicit !m_minimal_published guard keeps the tag-less guarantee from
7310 // depending on that erase happening to run first.
7311 stream << " <" << METADATA_TAG << " name=\"" << ORCASLICER_TAG << "\">"
7312 << xml_escape(SoftFever_VERSION) << "</" << METADATA_TAG << ">\n";
7313 }
7314 }
7315
7316 stream << " <" << RESOURCES_TAG << ">\n";
7317 std::string buf = stream.str();
7318 if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
7319 add_error("Unable to add model file to archive");
7320 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add model file to archive\n");
7321 return false;
7322 }
7323 }
7324
7325 // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
7326 BuildItemsList build_items;
7327
7328 // The object_id here is a one based identifier of the first instance of a ModelObject in the 3MF file, where
7329 // all the object instances of all ModelObjects are stored and indexed in a 1 based linear fashion.
7330 // Therefore the list of object_ids here may not be continuous.
7331 unsigned int object_id = 1;
7332 unsigned int object_index = 0;
7333
7334 bool cb_cancel = false;
7335 std::vector<std::string> object_paths;
7336 // if (!m_skip_model) {
7337 for (ModelObject* obj : model.objects) {
7338 if (sub_model && obj != objects_data.begin()->second.object) continue;
7339
7340 if (proFn) {
7341 proFn(EXPORT_STAGE_ADD_MODELS, object_index++, model.objects.size(), cb_cancel);
7342 if (cb_cancel)
7343 return false;
7344 }
7345
7346 if (obj == nullptr)
7347 continue;
7348
7349 // Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
7350 ObjectToObjectDataMap::iterator object_it = objects_data.begin();
7351
7352 if (!sub_model) {
7353 // For backup, use backup id as object id
7354 int backup_id = const_cast<Model&>(model).get_object_backup_id(*obj);
7355 if (m_from_backup_save) object_id = backup_id;
7356 object_it = objects_data.insert({obj, {obj, backup_id} }).first;
7357 auto & object_data = object_it->second;
7358
7359 if (m_split_model) {
7360 auto filename = boost::format("3D/Objects/%s_%d.model") % obj->name % backup_id;
7361 object_data.sub_path = "/" + filename.str();
7362 object_paths.push_back(filename.str());
7363 }
7364
7365 auto &volumes_objectID = object_data.volumes_objectID;
7366 unsigned int volume_id = object_id, volume_count = 0;
7367 for (ModelVolume *volume : obj->volumes) {
7368 if (volume == nullptr)
7369 continue;
7370 volume_count++;
7371 if (m_share_mesh) {
7372 auto iter = m_shared_meshes.find(volume->mesh_ptr().get());
7373 if (iter != m_shared_meshes.end())
7374 {
7375 const ModelVolume* shared_volume = iter->second.second;
7376 bool same_texture_paint = true;
7377 for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS) && same_texture_paint; ++slot)
7378 same_texture_paint = shared_volume->texture_displacement_facet(slot).equals(volume->texture_displacement_facet(slot));
7379 if ((shared_volume->supported_facets.equals(volume->supported_facets))
7380 && (shared_volume->seam_facets.equals(volume->seam_facets))
7381 && (shared_volume->mmu_segmentation_facets.equals(volume->mmu_segmentation_facets))
7382 && (shared_volume->fuzzy_skin_facets.equals(volume->fuzzy_skin_facets))
7383 && same_texture_paint)
7384 {
7385 auto data = iter->second.first;
7386 const_cast<_BBS_3MF_Exporter *>(this)->m_volume_paths.insert({volume, {data->sub_path, data->volumes_objectID.find(iter->second.second)->second}});
7387 volumes_objectID.insert({volume, 0});
7388 object_data.share_mesh = true;
7389 continue;
7390 }
7391 }
7392 const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr().get(), {&object_data, volume}});
7393 }
7395 volume_id = (volume_count << 16 | backup_id);
7396 volumes_objectID.insert({volume, volume_id});
7397 volume_id++;
7398 }
7399
7400 if (!m_from_backup_save) object_id = volume_id;
7401 object_data.object_id = object_id;
7402 }
7403
7404 if (m_skip_model) continue;
7405
7406 if (write_object) {
7407 // Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
7408 // object_it->second.volumes_objectID will contain the offsets of the ModelVolumes in that single indexed triangle set.
7409 // object_id will be increased to point to the 1st instance of the next ModelObject.
7410 if (!_add_object_to_model_stream(context, object_it->second)) {
7411 add_error("Unable to add object to archive");
7412 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add object to archive\n");
7413 return false;
7414 }
7415 }
7416
7417 if (sub_model) break;
7418
7419 unsigned int count = 0;
7420 for (const ModelInstance* instance : obj->instances) {
7421 Transform3d t = instance->get_matrix();
7422 // instance_id is just a 1 indexed index in build_items.
7423 //assert(m_skip_static || curr_id == build_items.size() + 1);
7424
7425 build_items.emplace_back("", object_it->second.object_id, t, instance->printable);
7426 count++;
7427 }
7428
7429 if (!m_from_backup_save) object_id++;
7430 }
7431 // }
7432
7433 {
7434 std::stringstream stream;
7435 reset_stream(stream);
7436
7437 if (!m_skip_model && !sub_model) {
7438 for (auto object : model.objects) {
7439 auto &data = objects_data[object];
7441 }
7442 }
7443
7444 stream << " </" << RESOURCES_TAG << ">\n";
7445
7446 // Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
7447 if (!_add_build_to_model_stream(stream, build_items)) {
7448 add_error("Unable to add build to archive");
7449 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add build to archive\n");
7450 return false;
7451 }
7452
7453 stream << "</" << MODEL_TAG << ">\n";
7454
7455 std::string buf = stream.str();
7456
7457 if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
7458 ! mz_zip_writer_add_staged_finish(&context)) {
7459 add_error("Unable to add model file to archive");
7460 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add model file to archive\n");
7461 return false;
7462 }
7463 }
7464
7465 if (m_skip_model || write_object) return true;
7466
7467 // write model rels
7468 _add_relationships_file_to_archive(archive, MODEL_RELS_FILE, object_paths, {"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"});
7469
7470 if (!m_from_backup_save) {
7471 boost::mutex mutex;
7472 tbb::parallel_for(tbb::blocked_range<size_t>(0, objects_data.size(), 1), [this, &mutex, &model, objects = model.objects, &objects_data, &object_paths, main = &archive, project](const tbb::blocked_range<size_t>& range) {
7473 for (size_t i = range.begin(); i < range.end(); ++i) {
7474 auto iter = objects_data.find(objects[i]);
7475 ObjectToObjectDataMap objects_data2;
7476 objects_data2.insert(*iter);
7477 auto & object = *iter->second.object;
7478 mz_zip_archive archive;
7479 mz_zip_zero_struct(&archive);
7480 mz_zip_writer_init_heap(&archive, 0, 1024 * 1024);
7481 CNumericLocalesSetter locales_setter;
7482 _add_model_file_to_archive(object_paths[i], archive, model, objects_data2, nullptr, project);
7483 iter->second = objects_data2.begin()->second;
7484 void *ppBuf; size_t pSize;
7485 mz_zip_writer_finalize_heap_archive(&archive, &ppBuf, &pSize);
7486 mz_zip_writer_end(&archive);
7487 mz_zip_zero_struct(&archive);
7488 mz_zip_reader_init_mem(&archive, ppBuf, pSize, 0);
7489 {
7490 boost::unique_lock l(mutex);
7491 mz_zip_writer_add_from_zip_reader(main, &archive, 0);
7492 }
7493 mz_zip_reader_end(&archive);
7494 }
7495 });
7496 }
7497
7498 return true;
7499 }
const std::string MODEL_FILE
Definition 3mf.cpp:97
static constexpr const char * RESOURCES_TAG
Definition 3mf.cpp:110
static constexpr const char * MODEL_TAG
Definition 3mf.cpp:109
const std::string ORCASLICER_TAG
Definition bbs_3mf.cpp:199
const std::string BBL_DESIGNER_TAG
Definition bbs_3mf.cpp:186
const std::string BBL_MODEL_NAME_TAG
Definition bbs_3mf.cpp:184
const char * BBS_3MF_VERSION1
Definition bbs_3mf.cpp:172
const unsigned int VERSION_BBS_3MF
Definition bbs_3mf.cpp:169
const std::string BBL_DESIGNER_USER_ID_TAG
Definition bbs_3mf.cpp:187
const std::string BBL_CREATION_DATE_TAG
Definition bbs_3mf.cpp:195
const std::string BBL_MAKERLAB_VERSION_TAG
Definition bbs_3mf.cpp:201
const std::string BBL_APPLICATION_TAG
Definition bbs_3mf.cpp:197
const std::string BBL_MODIFICATION_TAG
Definition bbs_3mf.cpp:194
const char * BBS_3MF_VERSION
Definition bbs_3mf.cpp:173
const std::string BBL_REGION_TAG
Definition bbs_3mf.cpp:193
const std::string BBL_MAKERLAB_TAG
Definition bbs_3mf.cpp:200
const std::string BBL_COPYRIGHT_NORMATIVE_TAG
Definition bbs_3mf.cpp:191
const std::string BBL_DESIGNER_COVER_FILE_TAG
Definition bbs_3mf.cpp:188
const std::string BBL_MODEL_ID_TAG
Definition bbs_3mf.cpp:183
const std::string BBL_LICENSE_TAG
Definition bbs_3mf.cpp:192
const std::string BBL_ORIGIN_TAG
Definition bbs_3mf.cpp:185
const std::string BBL_DESCRIPTION_TAG
Definition bbs_3mf.cpp:189
#define WRITE_ZIP_LANGUAGE_ENCODING
Definition bbs_3mf.cpp:126
const std::string MODEL_RELS_FILE
Definition bbs_3mf.cpp:213
bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, ObjectData const &object_data) const
Definition bbs_3mf.cpp:7501
std::vector< BuildItem > BuildItemsList
Definition bbs_3mf.cpp:6217
void _add_object_components_to_stream(std::stringstream &stream, ObjectData const &object_data) const
Definition bbs_3mf.cpp:7527
bool _add_build_to_model_stream(std::stringstream &stream, const BuildItemsList &build_items) const
Definition bbs_3mf.cpp:7854
bool m_from_backup_save
Definition bbs_3mf.cpp:6224
std::map< void const *, std::pair< ObjectData *, ModelVolume const * > > m_shared_meshes
Definition bbs_3mf.cpp:6234
bool m_minimal_published
Definition bbs_3mf.cpp:6229
bool m_split_model
Definition bbs_3mf.cpp:6225
bool m_zip64
Definition bbs_3mf.cpp:6221
int main(int argc, char const *argv[])
Definition encoding-check.cpp:129
void trace(unsigned int level, const char *message)
Definition utils.cpp:247
const int EXPORT_STAGE_ADD_MODELS
Definition bbs_3mf.hpp:247

References _add_model_file_to_archive(), _add_object_to_model_stream(), Slic3r::_BBS_3MF_Base::add_error(), BBL_APPLICATION_TAG, BBL_COPYRIGHT_NORMATIVE_TAG, BBL_CREATION_DATE_TAG, BBL_DESCRIPTION_TAG, BBL_DESIGNER_COVER_FILE_TAG, BBL_DESIGNER_TAG, BBL_DESIGNER_USER_ID_TAG, BBL_LICENSE_TAG, BBL_MAKERLAB_TAG, BBL_MAKERLAB_VERSION_TAG, BBL_MODEL_ID_TAG, BBL_MODEL_NAME_TAG, BBL_MODIFICATION_TAG, BBL_ORIGIN_TAG, BBL_REGION_TAG, BBS_3MF_VERSION, BBS_3MF_VERSION1, count, Slic3r::Model::design_info, ZipUnicodePathExtraField::encode(), Slic3r::encode_path(), Slic3r::FacetsAnnotation::equals(), Slic3r::EXPORT_STAGE_ADD_MODELS, Slic3r::ModelVolume::fuzzy_skin_facets, m_from_backup_save, m_minimal_published, m_production_ext, m_share_mesh, m_shared_meshes, m_skip_model, m_split_model, m_volume_paths, m_zip64, Slic3r::Model::md_name, Slic3r::Model::md_value, Slic3r::ModelVolume::mesh_ptr(), METADATA_TAG, Slic3r::Model::mk_name, Slic3r::Model::mk_version, Slic3r::ModelVolume::mmu_segmentation_facets, MODEL_FILE, Slic3r::Model::model_info, MODEL_TAG, Slic3r::Model::objects, ORCASLICER_TAG, Slic3r::BBLProject::project_country_code, Slic3r::BBLProject::project_model_id, Slic3r::reset_stream(), RESOURCES_TAG, Slic3r::ModelVolume::seam_facets, Slic3r::ModelVolume::supported_facets, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, Slic3r::trace(), VERSION_BBS_3MF, WRITE_ZIP_LANGUAGE_ENCODING, and Slic3r::xml_escape().

Referenced by _add_model_file_to_archive(), _save_model_to_file(), and save_object_mesh().

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

◆ _add_object_components_to_stream()

void Slic3r::_BBS_3MF_Exporter::_add_object_components_to_stream ( std::stringstream & stream,
ObjectData const & object_data ) const
private
7528 {
7529 auto & object = *object_data.object;
7530
7531 stream << " <" << OBJECT_TAG << " id=\"" << object_data.object_id;
7532 if (m_production_ext)
7533 stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t)object_data.backup_id}
7534 << (object_data.share_mesh ? OBJECT_UUID_SUFFIX2 : OBJECT_UUID_SUFFIX);
7535 stream << "\" type=\"model\">\n";
7536
7537 stream << " <" << COMPONENTS_TAG << ">\n";
7538
7539 for (unsigned int index = 0; index < object.volumes.size(); index ++) {
7540 ModelVolume *volume = object.volumes[index];
7541 unsigned int volume_id = object_data.volumes_objectID.find(volume)->second;
7542 auto * ppath = &object_data.sub_path;
7543 auto iter = m_volume_paths.find(volume);
7544 if (iter != m_volume_paths.end()) {
7545 ppath = &iter->second.first;
7546 volume_id = iter->second.second;
7547 }
7548 //add the transform of the volume
7549 if (ppath->empty())
7550 stream << " <" << COMPONENT_TAG << " objectid=\"" << volume_id;
7551 else
7552 stream << " <" << COMPONENT_TAG << " p:path=\"" << xml_escape(*ppath) << "\" objectid=\"" << volume_id; // << "\"/>\n";
7553 if (m_production_ext)
7554 stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) (index + (object_data.backup_id << 16))} << COMPONENT_UUID_SUFFIX;
7555 const Transform3d &transf = volume->get_matrix();
7556 stream << "\" " << TRANSFORM_ATTR << "=\"";
7557 for (unsigned c = 0; c < 4; ++c) {
7558 for (unsigned r = 0; r < 3; ++r) {
7559 stream << transf(r, c);
7560 if (r != 2 || c != 3)
7561 stream << " ";
7562 }
7563 }
7564 stream << "\"/>\n";
7565 }
7566
7567 stream << " </" << COMPONENTS_TAG << ">\n";
7568
7569 stream << " </" << OBJECT_TAG << ">\n";
7570 }
static constexpr const char * COMPONENTS_TAG
Definition 3mf.cpp:117
static constexpr const char * COMPONENT_TAG
Definition 3mf.cpp:118
static constexpr const char * COMPONENT_UUID_SUFFIX
Definition bbs_3mf.cpp:331
static constexpr const char * OBJECT_UUID_SUFFIX
Definition bbs_3mf.cpp:328
static constexpr const char * OBJECT_UUID_SUFFIX2
Definition bbs_3mf.cpp:329

References _add_object_components_to_stream(), Slic3r::_BBS_3MF_Exporter::ObjectData::backup_id, COMPONENT_TAG, COMPONENT_UUID_SUFFIX, COMPONENTS_TAG, Slic3r::ModelVolume::get_matrix(), m_production_ext, m_volume_paths, Slic3r::_BBS_3MF_Exporter::ObjectData::object, Slic3r::_BBS_3MF_Exporter::ObjectData::object_id, OBJECT_TAG, OBJECT_UUID_SUFFIX, OBJECT_UUID_SUFFIX2, PUUID_ATTR, Slic3r::_BBS_3MF_Exporter::ObjectData::share_mesh, Slic3r::_BBS_3MF_Exporter::ObjectData::sub_path, TRANSFORM_ATTR, Slic3r::_BBS_3MF_Exporter::ObjectData::volumes_objectID, and Slic3r::xml_escape().

Referenced by _add_object_components_to_stream().

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

◆ _add_object_to_model_stream()

bool Slic3r::_BBS_3MF_Exporter::_add_object_to_model_stream ( mz_zip_writer_staged_context & context,
ObjectData const & object_data ) const
private
7502 {
7503 // backup: make _add_mesh_to_object_stream() reusable
7504 auto flush = [this, &context](std::string & buf, bool force = false) {
7505 if ((force && !buf.empty()) || buf.size() >= 65536 * 16) {
7506 if (!mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
7507 add_error("Error during writing or compression");
7508 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Error during writing or compression\n");
7509 return false;
7510 }
7511 buf.clear();
7512 }
7513 return true;
7514 };
7515 if (!_add_mesh_to_object_stream(flush, object_data)) {
7516 add_error("Unable to add mesh to archive");
7517 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add mesh to archive\n");
7518 return false;
7519 }
7520
7521 // Move all components to main model
7522 //_add_object_components_to_stream(stream, object_data);
7523
7524 return true;
7525 }
bool _add_mesh_to_object_stream(std::function< bool(std::string &, bool)> const &flush, ObjectData const &object_data) const
Definition bbs_3mf.cpp:7593

References _add_mesh_to_object_stream(), _add_object_to_model_stream(), and Slic3r::_BBS_3MF_Base::add_error().

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::_BBS_3MF_Exporter::_add_print_config_file_to_archive ( mz_zip_archive & archive,
const DynamicPrintConfig & config )
private
8177 {
8179 char buffer[1024];
8180 snprintf(buffer, 1024, "; %s\n\n", header_slic3r_generated().c_str());
8181 std::string out = buffer;
8182
8183 for (const std::string &key : config.keys())
8184 if (key != "compatible_printers")
8185 out += "; " + key + " = " + config.opt_serialize(key) + "\n";
8186
8187 if (!out.empty()) {
8188 if (!mz_zip_writer_add_mem(&archive, BBS_PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
8189 add_error("Unable to add print config file to archive");
8190 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add print config file to archive\n");
8191 return false;
8192 }
8193 }
8194
8195 return true;
8196 }
const std::string BBS_PRINT_CONFIG_FILE
Definition bbs_3mf.cpp:229
std::string header_slic3r_generated()
Definition utils.cpp:1470

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

Referenced by _add_print_config_file_to_archive(), and _add_sla_drain_holes_file_to_archive().

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

◆ _add_project_config_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_project_config_file_to_archive ( mz_zip_archive & archive,
const DynamicPrintConfig & config,
Model & model )
private
8200 {
8201 const std::string& temp_path = model.get_backup_path();
8202 std::string temp_file = temp_path + std::string("/") + "_temp_1.config";
8203 config.save_to_json(temp_file, std::string("project_settings"), std::string("project"), std::string(SLIC3R_VERSION));
8204 return _add_file_to_archive(archive, BBS_PROJECT_CONFIG_FILE, temp_file);
8205 }
const std::string BBS_PROJECT_CONFIG_FILE
Definition bbs_3mf.cpp:230

References _add_file_to_archive(), _add_project_config_file_to_archive(), BBS_PROJECT_CONFIG_FILE, Slic3r::Model::get_backup_path(), and Slic3r::ConfigBase::save_to_json().

Referenced by _add_project_config_file_to_archive(), _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_project_embedded_presets_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_project_embedded_presets_to_archive ( mz_zip_archive & archive,
Model & model,
std::vector< Preset * > project_presets )
private
8209 {
8210 char buffer[1024];
8211 snprintf(buffer, 1024, "; %s\n\n", header_slic3r_generated().c_str());
8212 std::string out = buffer;
8213 int print_count = 0, filament_count = 0, printer_count = 0;
8214 const std::string& temp_path = model.get_backup_path();
8215
8216 for (int i = 0; i < project_presets.size(); i++)
8217 {
8218 Preset* preset = project_presets[i];
8219
8220 if (preset) {
8221 preset->file = temp_path + std::string("/") + "_temp_1.config";
8222 DynamicPrintConfig& config = preset->config;
8223 //config.save(preset->file);
8224 config.save_to_json(preset->file, preset->name, std::string("project"), preset->version.to_string());
8225
8226 std::string dest_file;
8227 if (preset->type == Preset::TYPE_PRINT) {
8228 dest_file = (boost::format(EMBEDDED_PRINT_FILE_FORMAT) % (print_count + 1)).str();
8229 print_count++;
8230 }
8231 else if (preset->type == Preset::TYPE_FILAMENT) {
8232 dest_file = (boost::format(EMBEDDED_FILAMENT_FILE_FORMAT) % (filament_count + 1)).str();
8233 filament_count++;
8234 }
8235 else if (preset->type == Preset::TYPE_PRINTER) {
8236 dest_file = (boost::format(EMBEDDED_PRINTER_FILE_FORMAT) % (printer_count + 1)).str();
8237 printer_count++;
8238 }
8239 else
8240 continue;
8241
8242 _add_file_to_archive(archive, dest_file, preset->file);
8243 }
8244 }
8245
8246 return true;
8247 }
#define EMBEDDED_PRINT_FILE_FORMAT
Definition bbs_3mf.hpp:42
#define EMBEDDED_PRINTER_FILE_FORMAT
Definition bbs_3mf.hpp:44
#define EMBEDDED_FILAMENT_FILE_FORMAT
Definition bbs_3mf.hpp:43
@ TYPE_PRINTER
Definition Preset.hpp:250
@ TYPE_PRINT
Definition Preset.hpp:246
@ TYPE_FILAMENT
Definition Preset.hpp:248
@ Preset
Definition calib.hpp:39

References _add_file_to_archive(), _add_project_embedded_presets_to_archive(), Slic3r::Preset::config, EMBEDDED_FILAMENT_FILE_FORMAT, EMBEDDED_PRINT_FILE_FORMAT, EMBEDDED_PRINTER_FILE_FORMAT, Slic3r::Preset::file, Slic3r::Model::get_backup_path(), Slic3r::header_slic3r_generated(), Slic3r::Preset::name, Slic3r::ConfigBase::save_to_json(), Slic3r::Semver::to_string(), Slic3r::Preset::type, Slic3r::Preset::TYPE_FILAMENT, Slic3r::Preset::TYPE_PRINT, Slic3r::Preset::TYPE_PRINTER, and Slic3r::Preset::version.

Referenced by _add_project_embedded_presets_to_archive(), _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_relationships_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_relationships_file_to_archive ( mz_zip_archive & archive,
std::string const & from = {},
std::vector< std::string > const & targets = {},
std::vector< std::string > const & types = {},
PackingTemporaryData data = PackingTemporaryData(),
int export_plate_idx = -1,
bool plate_thumbnail_written = false,
bool plate_small_thumbnail_written = false ) const
private
7065 {
7066 std::stringstream stream;
7067 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
7068 stream << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n";
7069 if (from.empty()) {
7070 stream << " <Relationship Target=\"/" << MODEL_FILE << "\" Id=\"rel-1\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\"/>\n";
7071
7072 if (export_plate_idx < 0) {
7073 //use cover image if have
7074 if (data._3mf_thumbnail.empty()) {
7075 // ORCA: only reference the plate thumbnail if it was written
7076 if (plate_thumbnail_written)
7077 stream << " <Relationship Target=\"/Metadata/plate_1.png"
7078 << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
7079 } else {
7080 stream << " <Relationship Target=\"/" << xml_escape(data._3mf_thumbnail)
7081 << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
7082 }
7083
7084 if (data._3mf_printer_thumbnail_middle.empty()) {
7085 if (plate_thumbnail_written)
7086 stream << " <Relationship Target=\"/Metadata/plate_1.png"
7087 << "\" Id=\"rel-4\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-middle\"/>\n";
7088 } else {
7089 stream << " <Relationship Target=\"/" << xml_escape(data._3mf_printer_thumbnail_middle)
7090 << "\" Id=\"rel-4\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-middle\"/>\n";
7091 }
7092
7093 if (data._3mf_printer_thumbnail_small.empty()) {
7094 if (plate_small_thumbnail_written)
7095 stream << "<Relationship Target=\"/Metadata/plate_1_small.png"
7096 << "\" Id=\"rel-5\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-small\"/>\n";
7097 } else {
7098 stream << " <Relationship Target=\"/" << xml_escape(data._3mf_printer_thumbnail_small)
7099 << "\" Id=\"rel-5\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-small\"/>\n";
7100 }
7101 }
7102 else if (plate_thumbnail_written) {
7103 //always use plate thumbnails
7104 std::string thumbnail_file_str = (boost::format("Metadata/plate_%1%.png") % (export_plate_idx + 1)).str();
7105 stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
7106 << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
7107
7108 thumbnail_file_str = (boost::format("Metadata/plate_%1%.png") % (export_plate_idx + 1)).str();
7109 stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
7110 << "\" Id=\"rel-4\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-middle\"/>\n";
7111
7112 if (plate_small_thumbnail_written) {
7113 thumbnail_file_str = (boost::format("Metadata/plate_%1%_small.png") % (export_plate_idx + 1)).str();
7114 stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
7115 << "\" Id=\"rel-5\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-small\"/>\n";
7116 }
7117 }
7118 }
7119 else if (targets.empty()) {
7120 return false;
7121 }
7122 else {
7123 int i = 0;
7124 for (auto & path : targets) {
7125 for (auto & type : types)
7126 stream << " <Relationship Target=\"/" << xml_escape(path) << "\" Id=\"rel-" << std::to_string(++i) << "\" Type=\"" << type << "\"/>\n";
7127 }
7128 }
7129 stream << "</Relationships>";
7130
7131 std::string out = stream.str();
7132
7133 if (!mz_zip_writer_add_mem(&archive, from.empty() ? RELATIONSHIPS_FILE.c_str() : from.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
7134 add_error("Unable to add relationships file to archive");
7135 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add relationships file to archive\n");
7136 return false;
7137 }
7138
7139 return true;
7140 }
const std::string RELATIONSHIPS_FILE
Definition 3mf.cpp:99

References Slic3r::PackingTemporaryData::_3mf_printer_thumbnail_middle, Slic3r::PackingTemporaryData::_3mf_printer_thumbnail_small, Slic3r::PackingTemporaryData::_3mf_thumbnail, _add_relationships_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), MODEL_FILE, RELATIONSHIPS_FILE, and Slic3r::xml_escape().

Referenced by _add_model_config_file_to_archive(), _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::_BBS_3MF_Exporter::_add_sla_drain_holes_file_to_archive ( mz_zip_archive & archive,
Model & model )
private

◆ _add_sla_support_points_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_sla_support_points_file_to_archive ( mz_zip_archive & archive,
Model & model )
private

◆ _add_slice_info_config_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_slice_info_config_file_to_archive ( mz_zip_archive & archive,
const Model & model,
PlateDataPtrs & plate_data_list,
const ObjectToObjectDataMap & objects_data,
const DynamicPrintConfig & config )
private
8716 {
8717 std::stringstream stream;
8718 // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
8719 // when loaded as accurately as possible.
8720 stream << std::setprecision(std::numeric_limits<double>::max_digits10);
8721 stream << std::setiosflags(std::ios::fixed) << std::setprecision(2);
8722 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
8723 stream << "<" << CONFIG_TAG << ">\n";
8724
8725 // save slice header for debug
8726 stream << " <" << SLICE_HEADER_TAG << ">\n";
8727 stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Type" << "\" " << VALUE_ATTR << "=\"" << "slicer" << "\"/>\n";
8728 stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Version" << "\" " << VALUE_ATTR << "=\"" << convert_to_full_version(SLIC3R_VERSION) << "\"/>\n";
8729 stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "OrcaSlicer-Version" << "\" " << VALUE_ATTR << "=\"" << SoftFever_VERSION << "\"/>\n";
8730 stream << " </" << SLICE_HEADER_TAG << ">\n";
8731
8732 for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
8733 {
8734 PlateData* plate_data = plate_data_list[i];
8735 //int instance_size = plate_data->objects_and_instances.size();
8736
8737 if (plate_data != nullptr && plate_data->is_sliced_valid) {
8738 stream << " <" << PLATE_TAG << ">\n";
8739 //plate index
8740 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PLATE_IDX_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->plate_index + 1 << "\"/>\n";
8741
8742 int timelapse_type = int(config.opt_enum<TimelapseType>("timelapse_type"));
8743 for (auto it = plate_data->warnings.begin(); it != plate_data->warnings.end(); it++) {
8744 if (it->msg == NOT_GENERATE_TIMELAPSE) {
8745 timelapse_type = -1;
8746 break;
8747 }
8748 }
8749
8750 std::vector<int> extruder_types = config.option<ConfigOptionEnumsGeneric>("extruder_type")->values;
8751 std::vector<int> nozzle_volume_types = config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
8752 auto* nozzle_volume_type_option = dynamic_cast<const ConfigOptionEnumsGeneric*>(config.option("nozzle_volume_type"));
8753
8754 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << EXTRUDER_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"";
8755 add_vector(stream, extruder_types);
8756 stream << "\"/>\n";
8757
8758 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NOZZLE_VOLUME_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"";
8759 add_vector(stream, nozzle_volume_types);
8760 stream << "\"/>\n";
8761
8762 auto* nozzle_diameter_option = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
8763 std::string nozzle_diameters_str;
8764 if (nozzle_diameter_option)
8765 nozzle_diameters_str = nozzle_diameter_option->serialize();
8766
8767 // True when any extruder carries a cluster of interchangeable nozzles (max nozzle count
8768 // > 1). Such an extruder's per-nozzle diameters are not expressible in the per-extruder
8769 // nozzle_diameter config, so the saved <filament>/<nozzle> diameters must come from the
8770 // grouping result. For a single-nozzle-per-extruder printer the true diameter is the raw
8771 // config value; the grouping result rounds it to the nearest of {0.2,0.4,0.6,0.8} for its
8772 // internal matching key, so reading that back would rewrite a non-standard nozzle
8773 // (e.g. 0.5 -> 0.4). Use this flag to keep the exact config value in that case.
8774 auto* extruder_max_nozzle_count_option = dynamic_cast<const ConfigOptionInts*>(config.option("extruder_max_nozzle_count"));
8775 const bool has_multi_nozzle_extruder = extruder_max_nozzle_count_option &&
8776 std::any_of(extruder_max_nozzle_count_option->values.begin(), extruder_max_nozzle_count_option->values.end(),
8777 [](int v) { return v > 1; });
8778
8779 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINTER_MODEL_ID_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->printer_model_id << "\"/>\n";
8780 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NOZZLE_DIAMETERS_ATTR << "\" " << VALUE_ATTR << "=\"" << nozzle_diameters_str << "\"/>\n";
8781 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TIMELAPSE_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << timelapse_type << "\"/>\n";
8782 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SLICE_PREDICTION_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->get_gcode_prediction_str() << "\"/>\n";
8783 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SLICE_WEIGHT_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->get_gcode_weight_str() << "\"/>\n";
8784 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FIRST_LAYER_TIME_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->first_layer_time << "\"/>\n";
8785 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OUTSIDE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->toolpath_outside << "\"/>\n";
8786 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SUPPORT_USED_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->is_support_used << "\"/>\n";
8787 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << LABEL_OBJECT_ENABLED_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->is_label_object_enabled << "\"/>\n";
8788 // Report the plate's dynamic-map state from the grouping result. The result is present
8789 // for the whole fleet (a static single-nozzle result too), so this if-branch is normally
8790 // taken; is_support_dynamic_nozzle_map() is false for any non-dynamic (static /
8791 // single-extruder) result ⇒ byte-identical to the previously hard-coded value. The else
8792 // is a defensive fallback for a missing result.
8793 if (plate_data && plate_data->nozzle_group_result)
8794 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << ENABLE_FILAMENT_DYNAMIC_MAP_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << plate_data->nozzle_group_result->is_support_dynamic_nozzle_map() << "\"/>\n";
8795 else
8796 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << ENABLE_FILAMENT_DYNAMIC_MAP_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << false << "\"/>\n";
8797 {
8798 bool has_filament_switcher = config.has("has_filament_switcher") ? config.opt_bool("has_filament_switcher") : false;
8799 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << HAS_FILAMENT_SWITCHER_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << has_filament_switcher << "\"/>\n";
8800 }
8801
8802 std::vector<int> filament_maps = plate_data->filament_maps;
8803 if (filament_maps.empty())
8804 filament_maps = config.option<ConfigOptionInts>("filament_map")->values;
8805 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FILAMENT_MAP_ATTR << "\" " << VALUE_ATTR << "=\"";
8806 add_vector<int>(stream, filament_maps);
8807 stream << "\"/>\n";
8808
8809 if (plate_data->limit_filament_maps.size() > 0) {
8810 stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << LIMIT_FILAMENT_MAP_ATTR << "\" " << VALUE_ATTR << "=\"";
8811 add_vector<int>(stream, plate_data->limit_filament_maps);
8812 stream << "\"/>\n";
8813 }
8814
8815 // Orca: for non-BambuLab printers that label objects in the g-code (Klipper/Marlin/RRF via
8816 // EXCLUDE_OBJECT / M486), write the per-instance g-code object name into slice_info.config
8817 // (e.g. "OrcaCube_v2.drc_id_0_copy_0") so the printer can correlate objects between the 3MF
8818 // and the g-code. The g-code names objects per plate as
8819 // "<name>_id_<object index>_copy_<instance index>" (see GCode::set_object_info), which we
8820 // reconstruct here from the (sorted) objects_and_instances list. identify_id is unchanged.
8821 // BambuLab printers keep the raw object name.
8822 const GCodeFlavor slice_gcode_flavor = config.opt_enum<GCodeFlavor>("gcode_flavor");
8823 const bool use_gcode_object_name = !GCodeProcessor::s_IsBBLPrinter &&
8824 (slice_gcode_flavor == gcfKlipper || slice_gcode_flavor == gcfMarlinLegacy ||
8825 slice_gcode_flavor == gcfMarlinFirmware || slice_gcode_flavor == gcfRepRapFirmware);
8826 int gcode_object_index = -1;
8827 int gcode_copy_index = 0;
8828 int last_object_id = -1;
8829
8830 for (auto it = plate_data->objects_and_instances.begin(); it != plate_data->objects_and_instances.end(); it++)
8831 {
8832 int obj_id = it->first;
8833 int inst_id = it->second;
8834 int identify_id = 0;
8835 ModelObject* obj = NULL;
8836 ModelInstance* inst = NULL;
8837 if (obj_id >= model.objects.size()) {
8838 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid object id %1%\n")%obj_id;
8839 continue;
8840 }
8841 obj = model.objects[obj_id];
8842
8843 if (obj && (inst_id >= obj->instances.size())) {
8844 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid instance id %1%\n")%inst_id;
8845 continue;
8846 }
8847 inst = obj->instances[inst_id];
8848 if (m_use_loaded_id && (inst->loaded_id > 0))
8849 identify_id = inst->loaded_id;
8850 else
8851 identify_id = inst->id().id;
8852 bool skipped = std::find(plate_data->skipped_objects.begin(), plate_data->skipped_objects.end(), identify_id) !=
8853 plate_data->skipped_objects.end();
8854
8855 // Advance the per-plate g-code object/copy index. objects_and_instances is sorted,
8856 // so all instances of the same object are contiguous.
8857 if (obj_id != last_object_id) {
8858 ++gcode_object_index;
8859 gcode_copy_index = 0;
8860 last_object_id = obj_id;
8861 } else {
8862 ++gcode_copy_index;
8863 }
8864
8865 std::string object_name = obj->name;
8866 if (use_gcode_object_name)
8867 // Matches GCode::get_instance_name(): sanitize(sanitize(name) + "_id_<obj>_copy_<inst>").
8868 object_name = sanitize_object_label(sanitize_object_label(obj->name) + "_id_" +
8869 std::to_string(gcode_object_index) + "_copy_" + std::to_string(gcode_copy_index));
8870
8871 stream << " <" << OBJECT_TAG << " " << IDENTIFYID_ATTR << "=\"" << std::to_string(identify_id) << "\" " << NAME_ATTR << "=\"" << xml_escape(object_name)
8872 << "\" " << SKIPPED_ATTR << "=\"" << (skipped ? "true" : "false")
8873 << "\" />\n";
8874 }
8875
8876 auto get_nozzle_group_id = [&filament_maps](int filament_id) {
8877 if (filament_id >= 0 && filament_id < static_cast<int>(filament_maps.size()) && filament_maps[filament_id] > 0)
8878 return filament_maps[filament_id] - 1;
8879 return 0;
8880 };
8881 auto get_nozzle_diameter = [nozzle_diameter_option](int nozzle_group_id) {
8882 if (!nozzle_diameter_option || nozzle_diameter_option->values.empty())
8883 return 0.0;
8884 if (nozzle_group_id >= 0 && nozzle_group_id < static_cast<int>(nozzle_diameter_option->values.size()))
8885 return nozzle_diameter_option->values[nozzle_group_id];
8886 return nozzle_diameter_option->values.front();
8887 };
8888 auto get_nozzle_diameter_str = [&get_nozzle_diameter](int nozzle_group_id) {
8889 std::ostringstream diameter_stream;
8890 diameter_stream << std::defaultfloat << get_nozzle_diameter(nozzle_group_id);
8891 return diameter_stream.str();
8892 };
8893 auto get_nozzle_volume_type = [nozzle_volume_type_option](int nozzle_group_id) {
8894 if (!nozzle_volume_type_option || nozzle_volume_type_option->values.empty())
8895 return std::string();
8896 int nozzle_volume_type = nozzle_volume_type_option->values.front();
8897 if (nozzle_group_id >= 0 && nozzle_group_id < static_cast<int>(nozzle_volume_type_option->values.size()))
8898 nozzle_volume_type = nozzle_volume_type_option->values[nozzle_group_id];
8902 };
8903 std::vector<int> used_nozzle_groups;
8904
8905 for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++)
8906 {
8907 int nozzle_group_id = get_nozzle_group_id(it->id);
8908 if (std::find(used_nozzle_groups.begin(), used_nozzle_groups.end(), nozzle_group_id) == used_nozzle_groups.end())
8909 used_nozzle_groups.push_back(nozzle_group_id);
8910 const std::string filament_nozzle_group_id = it->group_id.empty() ? std::to_string(nozzle_group_id) : join_int_list_comma(it->group_id);
8911 // Single-nozzle extruders: exact config diameter; clusters keep the result's rounded
8912 // value (see has_multi_nozzle_extruder).
8913 const double filament_nozzle_diameter = (has_multi_nozzle_extruder && it->nozzle_diameter > 0.0)
8914 ? it->nozzle_diameter : get_nozzle_diameter(nozzle_group_id);
8915 const std::string filament_nozzle_volume_type = it->nozzle_volume_type.empty() ? get_nozzle_volume_type(nozzle_group_id) : it->nozzle_volume_type;
8916
8917 stream << " <" << FILAMENT_TAG << " " << FILAMENT_ID_TAG << "=\"" << std::to_string(it->id + 1) << "\" "
8918 << FILAMENT_TRAY_INFO_ID_TAG <<"=\""<< it->filament_id <<"\" "
8919 << FILAMENT_TYPE_TAG << "=\"" << it->type << "\" "
8920 << FILAMENT_COLOR_TAG << "=\"" << it->color << "\" "
8921 << FILAMENT_USED_M_TAG << "=\"" << it->used_m << "\" "
8922 << FILAMENT_USED_G_TAG << "=\"" << it->used_g << "\" "
8923 << FILAMENT_NOZZLE_GROUP_ID_TAG << "=\"" << filament_nozzle_group_id << "\" "
8924 << FILAMENT_NOZZLE_DIAMETER_TAG << "=\"" << filament_nozzle_diameter << "\" "
8925 << FILAMENT_NOZZLE_VOLUME_TYPE_TAG << "=\"" << filament_nozzle_volume_type << "\" "
8926 << FILAMENT_USED_FOR_OBJECT << "=\"" << std::boolalpha << it->used_for_object << "\" "
8927 << FILAMENT_USED_FOR_SUPPORT << "=\"" << std::boolalpha << it->used_for_support << "\"/>\n";
8928 }
8929
8930 // Mixed (virtual) filaments used by this plate. These are resolved to physical
8931 // components before g-code statistics, so they are not present in the <filament>
8932 // list above and are recorded separately here.
8933 for (auto it = plate_data->mixed_filaments_info.begin(); it != plate_data->mixed_filaments_info.end(); it++)
8934 {
8935 stream << " <" << MIXED_FILAMENT_TAG << " " << FILAMENT_ID_TAG << "=\"" << std::to_string(it->id) << "\" "
8936 << FILAMENT_TYPE_TAG << "=\"" << it->type << "\" "
8937 << FILAMENT_COLOR_TAG << "=\"" << it->color << "\" "
8938 << MIXED_FILAMENT_COMPONENTS_TAG << "=\"" << it->components << "\"/>\n";
8939 }
8940
8941 for (auto it = plate_data->warnings.begin(); it != plate_data->warnings.end(); it++) {
8942 stream << " <" << SLICE_WARNING_TAG << " msg=\"" << it->msg << "\" level=\"" << std::to_string(it->level) << "\" error_code =\"" << it->error_code << "\" />\n";
8943 }
8944
8945 // Emit the <nozzle> tags from the grouping result. Single-nozzle-per-extruder printers
8946 // override the diameter with the exact config value (see has_multi_nozzle_extruder); the
8947 // else is a defensive fallback for a missing result.
8948 if (plate_data->nozzle_group_result) {
8949 auto used_nozzle_list = plate_data->nozzle_group_result->get_used_nozzles_in_extruder();
8950 for (auto& used_nozzle : used_nozzle_list) {
8951 if (!has_multi_nozzle_extruder && nozzle_diameter_option &&
8952 used_nozzle.extruder_id >= 0 && used_nozzle.extruder_id < (int) nozzle_diameter_option->values.size()) {
8953 used_nozzle.diameter = get_nozzle_diameter_str(used_nozzle.extruder_id);
8954 }
8955 stream << " <" << NOZZLE_TAG << " " << used_nozzle.serialize() << "/>\n";
8956 }
8957 } else {
8958 for (int nozzle_group_id : used_nozzle_groups) {
8959 stream << " <" << NOZZLE_TAG << " "
8960 << "id=\"" << nozzle_group_id << "\" "
8961 << "extruder_id=\"" << nozzle_group_id + 1 << "\" "
8962 << "nozzle_diameter=\"" << get_nozzle_diameter_str(nozzle_group_id) << "\" "
8963 << "volume_type=\"" << get_nozzle_volume_type(nozzle_group_id) << "\"/>\n";
8964 }
8965 }
8966
8967 if (!plate_data->layer_filaments.empty()) {
8968 stream << " <" << LAYER_FILAMENT_LISTS_TAG << ">\n";
8969 for (auto iter = plate_data->layer_filaments.begin(); iter != plate_data->layer_filaments.end(); ++iter) {
8970 // key
8971 std::vector<unsigned int> sequence = iter->first;
8972 std::stringstream key_stream;
8973 add_vector(key_stream, sequence);
8974
8975 // value
8976 std::vector<std::pair<int, int>> ranges = iter->second;
8977 std::stringstream value_stream;
8978 for (size_t i = 0; i < ranges.size(); ++i) {
8979 value_stream << ranges[i].first;
8980 value_stream << " ";
8981 value_stream << ranges[i].second;
8982 if (i != (ranges.size() - 1)) value_stream << ",";
8983 }
8984
8985 stream << " <" << LAYER_FILAMENT_LIST_TAG << " filament_list=\"" << key_stream.str() << "\" layer_ranges=\"" << value_stream.str() << "\" />\n";
8986 }
8987 stream << " </" << LAYER_FILAMENT_LISTS_TAG << ">\n";
8988 }
8989
8990 stream << " </" << PLATE_TAG << ">\n";
8991 }
8992 }
8993 stream << "</" << CONFIG_TAG << ">\n";
8994
8995 std::string out = stream.str();
8996
8997 if (!mz_zip_writer_add_mem(&archive, SLICE_INFO_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
8998 add_error("Unable to add model config file to archive");
8999 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", store slice-info to 3mf, length %1%, failed\n") % out.length();
9000 return false;
9001 }
9002
9003 return true;
9004 }
static constexpr const char * NAME_ATTR
Definition 3mf.cpp:127
#define NOT_GENERATE_TIMELAPSE
Definition GCodeProcessor.hpp:48
static constexpr const char * SKIPPED_ATTR
Definition bbs_3mf.cpp:418
static constexpr const char * LABEL_OBJECT_ENABLED_ATTR
Definition bbs_3mf.cpp:415
static constexpr const char * SUPPORT_USED_ATTR
Definition bbs_3mf.cpp:414
static constexpr const char * MIXED_FILAMENT_COMPONENTS_TAG
Definition bbs_3mf.cpp:274
static constexpr const char * FILAMENT_ID_TAG
Definition bbs_3mf.cpp:277
static constexpr const char * FILAMENT_USED_FOR_OBJECT
Definition bbs_3mf.cpp:283
static constexpr const char * TIMELAPSE_TYPE_ATTR
Definition bbs_3mf.cpp:412
static constexpr const char * PRINTER_MODEL_ID_ATTR
Definition bbs_3mf.cpp:404
static constexpr const char * LIMIT_FILAMENT_MAP_ATTR
Definition bbs_3mf.cpp:390
static constexpr const char * ENABLE_FILAMENT_DYNAMIC_MAP_ATTR
Definition bbs_3mf.cpp:416
static constexpr const char * FILAMENT_NOZZLE_GROUP_ID_TAG
Definition bbs_3mf.cpp:284
static constexpr const char * PLATE_IDX_ATTR
Definition bbs_3mf.cpp:403
static constexpr const char * FILAMENT_USED_G_TAG
Definition bbs_3mf.cpp:281
static constexpr const char * FILAMENT_TYPE_TAG
Definition bbs_3mf.cpp:278
static constexpr const char * EXTRUDER_TYPE_ATTR
Definition bbs_3mf.cpp:405
static constexpr const char * NOZZLE_TAG
Definition bbs_3mf.cpp:287
static constexpr const char * NOZZLE_DIAMETERS_ATTR
Definition bbs_3mf.cpp:408
const std::string SLICE_INFO_CONFIG_FILE
Definition bbs_3mf.cpp:233
static constexpr const char * SLICE_HEADER_TAG
Definition bbs_3mf.cpp:301
static constexpr const char * HAS_FILAMENT_SWITCHER_ATTR
Definition bbs_3mf.cpp:417
static constexpr const char * FILAMENT_NOZZLE_VOLUME_TYPE_TAG
Definition bbs_3mf.cpp:286
static constexpr const char * SLICE_WARNING_TAG
Definition bbs_3mf.cpp:275
static constexpr const char * SLICE_PREDICTION_ATTR
Definition bbs_3mf.cpp:409
void add_vector(std::stringstream &stream, const std::vector< T > &values)
Definition bbs_3mf.cpp:576
static constexpr const char * FILAMENT_COLOR_TAG
Definition bbs_3mf.cpp:279
static constexpr const char * SLICE_WEIGHT_ATTR
Definition bbs_3mf.cpp:410
static constexpr const char * LAYER_FILAMENT_LIST_TAG
Definition bbs_3mf.cpp:290
static constexpr const char * SLICE_HEADER_ITEM_TAG
Definition bbs_3mf.cpp:302
static constexpr const char * FILAMENT_NOZZLE_DIAMETER_TAG
Definition bbs_3mf.cpp:285
static constexpr const char * MIXED_FILAMENT_TAG
Definition bbs_3mf.cpp:273
static constexpr const char * LAYER_FILAMENT_LISTS_TAG
Definition bbs_3mf.cpp:289
std::string join_int_list_comma(const std::vector< int > &values)
Definition bbs_3mf.cpp:608
static constexpr const char * FILAMENT_USED_M_TAG
Definition bbs_3mf.cpp:280
static constexpr const char * FILAMENT_TAG
Definition bbs_3mf.cpp:272
static constexpr const char * OUTSIDE_ATTR
Definition bbs_3mf.cpp:413
static constexpr const char * FILAMENT_TRAY_INFO_ID_TAG
Definition bbs_3mf.cpp:288
static constexpr const char * FIRST_LAYER_TIME_ATTR
Definition bbs_3mf.cpp:411
static constexpr const char * NOZZLE_VOLUME_TYPE_ATTR
Definition bbs_3mf.cpp:406
static constexpr const char * FILAMENT_USED_FOR_SUPPORT
Definition bbs_3mf.cpp:282
static bool s_IsBBLPrinter
Definition GCodeProcessor.hpp:650
bool m_use_loaded_id
Definition bbs_3mf.cpp:6230
GCodeFlavor
Definition PrintConfig.hpp:51
@ gcfMarlinFirmware
Definition PrintConfig.hpp:56
@ gcfKlipper
Definition PrintConfig.hpp:53
@ gcfRepRapFirmware
Definition PrintConfig.hpp:54
@ gcfMarlinLegacy
Definition PrintConfig.hpp:52
NozzleVolumeType
Definition PrintConfig.hpp:618
@ nvtMaxNozzleVolumeType
Definition PrintConfig.hpp:629
@ nvtStandard
Definition PrintConfig.hpp:619
uint32_t group_id(std::vector< Bridge > &bridges, uint32_t src_id)
Definition LayerRegion.cpp:214
ConfigOptionEnumsGenericTempl< false > ConfigOptionEnumsGeneric
Definition Config.hpp:2311
TimelapseType
Definition PrintConfig.hpp:468
zaa_minimize_perimeter_height nozzle_volume_type((ConfigOptionStrings, extruder_ams_count))((ConfigOptionStrings
std::string convert_to_full_version(std::string short_version)
Definition Utils.hpp:137
static std::string sanitize_object_label(const std::string &name)
Definition bbs_3mf.cpp:8703
std::string get_nozzle_volume_type_string(NozzleVolumeType nozzle_volume_type)
Definition PrintConfig.cpp:860
ConfigOptionFloatsTempl< false > ConfigOptionFloats
Definition Config.hpp:1015

References _add_slice_info_config_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), add_vector(), CONFIG_TAG, Slic3r::convert_to_full_version(), ENABLE_FILAMENT_DYNAMIC_MAP_ATTR, EXTRUDER_TYPE_ATTR, FILAMENT_COLOR_TAG, FILAMENT_ID_TAG, FILAMENT_MAP_ATTR, Slic3r::PlateData::filament_maps, FILAMENT_NOZZLE_DIAMETER_TAG, FILAMENT_NOZZLE_GROUP_ID_TAG, FILAMENT_NOZZLE_VOLUME_TYPE_TAG, FILAMENT_TAG, FILAMENT_TRAY_INFO_ID_TAG, FILAMENT_TYPE_TAG, FILAMENT_USED_FOR_OBJECT, FILAMENT_USED_FOR_SUPPORT, FILAMENT_USED_G_TAG, FILAMENT_USED_M_TAG, Slic3r::PlateData::first_layer_time, FIRST_LAYER_TIME_ATTR, Slic3r::gcfKlipper, Slic3r::gcfMarlinFirmware, Slic3r::gcfMarlinLegacy, Slic3r::gcfRepRapFirmware, Slic3r::PlateData::get_gcode_prediction_str(), Slic3r::PlateData::get_gcode_weight_str(), Slic3r::get_nozzle_volume_type_string(), Slic3r::ConfigOptionResolver::has(), HAS_FILAMENT_SWITCHER_ATTR, Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, IDENTIFYID_ATTR, Slic3r::ModelObject::instances, Slic3r::PlateData::is_label_object_enabled, Slic3r::PlateData::is_sliced_valid, Slic3r::PlateData::is_support_used, join_int_list_comma(), KEY_ATTR, LABEL_OBJECT_ENABLED_ATTR, LAYER_FILAMENT_LIST_TAG, LAYER_FILAMENT_LISTS_TAG, Slic3r::PlateData::layer_filaments, LIMIT_FILAMENT_MAP_ATTR, Slic3r::PlateData::limit_filament_maps, Slic3r::ModelInstance::loaded_id, m_use_loaded_id, METADATA_TAG, MIXED_FILAMENT_COMPONENTS_TAG, MIXED_FILAMENT_TAG, Slic3r::PlateData::mixed_filaments_info, Slic3r::ModelObject::name, NAME_ATTR, NOT_GENERATE_TIMELAPSE, NOZZLE_DIAMETERS_ATTR, Slic3r::PlateData::nozzle_group_result, NOZZLE_TAG, Slic3r::nozzle_volume_type(), NOZZLE_VOLUME_TYPE_ATTR, Slic3r::nvtMaxNozzleVolumeType, Slic3r::nvtStandard, OBJECT_TAG, Slic3r::Model::objects, Slic3r::PlateData::objects_and_instances, Slic3r::DynamicConfig::opt_bool(), Slic3r::DynamicConfig::opt_enum(), Slic3r::ConfigBase::option(), OUTSIDE_ATTR, PLATE_IDX_ATTR, Slic3r::PlateData::plate_index, PLATE_TAG, Slic3r::PlateData::printer_model_id, PRINTER_MODEL_ID_ATTR, Slic3r::GCodeProcessor::s_IsBBLPrinter, Slic3r::sanitize_object_label(), SKIPPED_ATTR, Slic3r::PlateData::skipped_objects, Slic3r::PlateData::slice_filaments_info, SLICE_HEADER_ITEM_TAG, SLICE_HEADER_TAG, SLICE_INFO_CONFIG_FILE, SLICE_PREDICTION_ATTR, SLICE_WARNING_TAG, SLICE_WEIGHT_ATTR, SUPPORT_USED_ATTR, TIMELAPSE_TYPE_ATTR, Slic3r::PlateData::toolpath_outside, VALUE_ATTR, Slic3r::ConfigOptionVector< int >::values, Slic3r::PlateData::warnings, and Slic3r::xml_escape().

Referenced by _add_sla_drain_holes_file_to_archive(), _add_slice_info_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_thumbnail_file_to_archive()

bool Slic3r::_BBS_3MF_Exporter::_add_thumbnail_file_to_archive ( mz_zip_archive & archive,
const ThumbnailData & thumbnail_data,
const char * local_path,
int index,
bool generate_small_thumbnail = false )
private
6960 {
6961 bool res = false;
6962
6963 size_t png_size = 0;
6964 void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_COMPRESSION, 1);
6965 if (png_data != nullptr) {
6966 std::string thumbnail_name = (boost::format("%1%_%2%.png")%local_path % (index + 1)).str();
6967 res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)png_data, png_size, MZ_NO_COMPRESSION);
6968 mz_free(png_data);
6969 }
6970
6971 if (!res) {
6972 add_error("Unable to add thumbnail file to archive");
6973 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add thumbnail file to archive\n");
6974 }
6975
6976 if (generate_small_thumbnail && thumbnail_data.is_valid()) {
6977 //generate small size of thumbnail
6978 std::vector<unsigned char> small_pixels;
6980 /* step width and step height */
6981 int sw = thumbnail_data.width / PLATE_THUMBNAIL_SMALL_WIDTH;
6982 int sh = thumbnail_data.height / PLATE_THUMBNAIL_SMALL_HEIGHT;
6983 int clampped_width = sw * PLATE_THUMBNAIL_SMALL_WIDTH;
6984 int clampped_height = sh * PLATE_THUMBNAIL_SMALL_HEIGHT;
6985
6986 for (int i = 0; i < clampped_height; i += sh) {
6987 for (int j = 0; j < clampped_width; j += sw) {
6988 int r = 0, g = 0, b = 0, a = 0;
6989 for (int m = 0; m < sh; m++) {
6990 for (int n = 0; n < sw; n++) {
6991 r += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 0];
6992 g += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 1];
6993 b += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 2];
6994 a += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 3];
6995 }
6996 }
6997 r = std::clamp(0, r / sw / sh, 255);
6998 g = std::clamp(0, g / sw / sh, 255);
6999 b = std::clamp(0, b / sw / sh, 255);
7000 a = std::clamp(0, a / sw / sh, 255);
7001 small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 0] = (unsigned char)r;
7002 small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 1] = (unsigned char)g;
7003 small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 2] = (unsigned char)b;
7004 small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 3] = (unsigned char)a;
7005 //memcpy((void*)&small_pixels[4*(i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh)], thumbnail_data.pixels.data() + 4*(i * thumbnail_data.width + j), 4);
7006 }
7007 }
7008 size_t small_png_size = 0;
7009 void* small_png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)small_pixels.data(), PLATE_THUMBNAIL_SMALL_WIDTH, PLATE_THUMBNAIL_SMALL_HEIGHT, 4, &small_png_size, MZ_DEFAULT_COMPRESSION, 1);
7010 if (png_data != nullptr) {
7011 std::string thumbnail_name = (boost::format("%1%_%2%_small.png") % local_path % (index + 1)).str();
7012 res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)small_png_data, small_png_size, MZ_NO_COMPRESSION);
7013 mz_free(small_png_data);
7014 }
7015
7016 if (!res) {
7017 add_error("Unable to add small thumbnail file to archive");
7018 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add small thumbnail file to archive\n");
7019 }
7020 }
7021
7022 return res;
7023 }
#define PLATE_THUMBNAIL_SMALL_WIDTH
Definition bbs_3mf.hpp:31
#define PLATE_THUMBNAIL_SMALL_HEIGHT
Definition bbs_3mf.hpp:32

References _add_thumbnail_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::ThumbnailData::height, Slic3r::ThumbnailData::is_valid(), Slic3r::ThumbnailData::pixels, PLATE_THUMBNAIL_SMALL_HEIGHT, PLATE_THUMBNAIL_SMALL_WIDTH, 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::_BBS_3MF_Exporter::_save_model_to_file ( const std::string & filename,
Model & model,
PlateDataPtrs & plate_data_list,
std::vector< Preset * > & project_presets,
const DynamicPrintConfig * config,
const std::vector< ThumbnailData * > & thumbnail_data,
const std::vector< ThumbnailData * > & no_light_thumbnail_data,
const std::vector< ThumbnailData * > & top_thumbnail_data,
const std::vector< ThumbnailData * > & pick_thumbnail_data,
Export3mfProgressFn proFn,
const std::vector< ThumbnailData * > & calibration_data,
const std::vector< PlateBBoxData * > & id_bboxes,
BBLProject * project = nullptr,
int export_plate_idx = -1 )
private
6432 {
6433 PackingTemporaryData temp_data;
6434
6435 mz_zip_archive archive;
6436 mz_zip_zero_struct(&archive);
6437
6438 bool cb_cancel = false;
6439
6440 //BBS progress point
6441 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ <<
6442 boost::format(",before open zip writer, m_skip_static %1%, m_save_gcode %2%, m_use_loaded_id %3%")%m_skip_static %m_save_gcode %m_use_loaded_id;
6443 if (proFn) {
6444 proFn(EXPORT_STAGE_OPEN_3MF, 0, 1, cb_cancel);
6445 if (cb_cancel)
6446 return false;
6447 }
6448
6449 if (!open_zip_writer(&archive, filename)) {
6450 add_error("Unable to open the file"+filename);
6451 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n");
6452 return false;
6453 }
6454
6455 struct close_lock
6456 {
6457 mz_zip_archive & archive;
6458 std::string const * filename;
6459 ~close_lock() {
6460 close_zip_writer(&archive);
6461 if (filename) {
6462 boost::system::error_code ec;
6463 boost::filesystem::remove(*filename, ec);
6464 }
6465 }
6466 } lock{ archive, &filename};
6467
6468 //BBS progress point
6469 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add _add_content_types_file_to_archive\n");
6470 if (proFn) {
6471 proFn(EXPORT_STAGE_CONTENT_TYPES, 0, 1, cb_cancel);
6472 if (cb_cancel)
6473 return false;
6474 }
6475
6476 // Adds content types file ("[Content_Types].xml";).
6477 // The content of this file is the same for each OrcaSlicer 3mf.
6478 if (!_add_content_types_file_to_archive(archive)) {
6479 return false;
6480 }
6481
6482 //BBS progress point
6483 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(",before add thumbnails, count %1%") % thumbnail_data.size();
6484 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",top&&pick thumbnails, count %1%")%top_thumbnail_data.size();
6485
6486 //BBS: add thumbnail for each plate
6487 //ORCA: the relationships file points at plate 1's thumbnail for a whole-project export,
6488 // otherwise at the exported plate's. Track whether that thumbnail was written.
6489 const size_t rels_plate_idx = (export_plate_idx < 0) ? 0 : static_cast<size_t>(export_plate_idx);
6490 bool rels_thumbnail_written = false;
6491 bool rels_small_thumbnail_written = false;
6492 if (!m_skip_static) {
6493 std::vector<bool> thumbnail_status(plate_data_list.size(), false);
6494 std::vector<bool> no_light_thumbnail_status(plate_data_list.size(), false);
6495 std::vector<bool> top_thumbnail_status(plate_data_list.size(), false);
6496 std::vector<bool> pick_thumbnail_status(plate_data_list.size(), false);
6497
6498 if ((thumbnail_data.size() > 0)&&(thumbnail_data.size() > plate_data_list.size())) {
6499 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", thumbnail_data size %1% > plate count %2%")
6500 % thumbnail_data.size() %plate_data_list.size();
6501 return false;
6502 }
6503 if ((no_light_thumbnail_data.size() > 0) && (no_light_thumbnail_data.size() > plate_data_list.size())) {
6504 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", no_light_thumbnail_data size %1% > plate count %2%") %
6505 no_light_thumbnail_data.size() % plate_data_list.size();
6506 return false;
6507 }
6508 if ((top_thumbnail_data.size() > 0)&&(top_thumbnail_data.size() > plate_data_list.size())) {
6509 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_data size %1% > plate count %2%")
6510 % top_thumbnail_data.size() %plate_data_list.size();
6511 return false;
6512 }
6513 if ((pick_thumbnail_data.size() > 0)&&(pick_thumbnail_data.size() > plate_data_list.size())) {
6514 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", pick_thumbnail_data size %1% > plate count %2%")
6515 % pick_thumbnail_data.size() %plate_data_list.size();
6516 return false;
6517 }
6518 if (top_thumbnail_data.size() != pick_thumbnail_data.size()) {
6519 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_data size %1% != pick_thumbnail_data size %2%")
6520 % top_thumbnail_data.size() %pick_thumbnail_data.size();
6521 return false;
6522 }
6523
6524 if (proFn) {
6525 proFn(EXPORT_STAGE_ADD_THUMBNAILS, 0, plate_data_list.size(), cb_cancel);
6526 if (cb_cancel)
6527 return false;
6528 }
6529
6530 for (unsigned int index = 0; index < thumbnail_data.size(); index++)
6531 {
6532 if (thumbnail_data[index]->is_valid())
6533 {
6534 if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data[index], "Metadata/plate", index, true)) {
6535 return false;
6536 }
6537
6538 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add thumbnail %1%'s data into 3mf")%(index+1);
6539 thumbnail_status[index] = true;
6540 if (static_cast<size_t>(index) == rels_plate_idx) {
6541 //ORCA: this call also writes the _small companion
6542 rels_thumbnail_written = true;
6543 rels_small_thumbnail_written = true;
6544 }
6545 }
6546 }
6547
6548 for (unsigned int index = 0; index < no_light_thumbnail_data.size(); index++) {
6549 if (no_light_thumbnail_data[index]->is_valid()) {
6550 if (!_add_thumbnail_file_to_archive(archive, *no_light_thumbnail_data[index], "Metadata/plate_no_light", index)) {
6551 return false;
6552 }
6553
6554 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(",add no light thumbnail %1%'s data into 3mf") % (index + 1);
6555 no_light_thumbnail_status[index] = true;
6556 }
6557 }
6558 // Adds the file Metadata/top_i.png and Metadata/pick_i.png
6559 for (unsigned int index = 0; index < top_thumbnail_data.size(); index++)
6560 {
6561 if (top_thumbnail_data[index]->is_valid())
6562 {
6563 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add top thumbnail %1%'s data into 3mf")%(index+1);
6564 if (!_add_thumbnail_file_to_archive(archive, *top_thumbnail_data[index], "Metadata/top", index)) {
6565 return false;
6566 }
6567 top_thumbnail_status[index] = true;
6568 }
6569
6570 if (pick_thumbnail_data[index]->is_valid())
6571 {
6572 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add pick thumbnail %1%'s data into 3mf")%(index+1);
6573 if (!_add_thumbnail_file_to_archive(archive, *pick_thumbnail_data[index], "Metadata/pick", index)) {
6574 return false;
6575 }
6576 pick_thumbnail_status[index] = true;
6577 }
6578 }
6579
6580 for (int i = 0; i < plate_data_list.size(); i++) {
6581 PlateData *plate_data = plate_data_list[i];
6582
6583 if (!thumbnail_status[i] && !plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))){
6584 std::string dst_in_3mf = (boost::format("Metadata/plate_%1%.png") % (i + 1)).str();
6585 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add thumbnail %1% from file %2%") % (i+1) %plate_data->thumbnail_file;
6586
6587 if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->thumbnail_file)) {
6588 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add thumbnail %1% from file %2% failed\n") % (i+1) %plate_data->thumbnail_file;
6589 return false;
6590 }
6591 //ORCA: copied from file, so there is no _small companion for this plate
6592 if (static_cast<size_t>(i) == rels_plate_idx)
6593 rels_thumbnail_written = true;
6594 }
6595
6596 if (!no_light_thumbnail_status[i] && !plate_data->no_light_thumbnail_file.empty() && (boost::filesystem::exists(plate_data->no_light_thumbnail_file))){
6597 std::string dst_in_3mf = (boost::format("Metadata/plate_no_light_%1%.png") % (i + 1)).str();
6598 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add no light thumbnail %1% from file %2%") % (i+1) %plate_data->no_light_thumbnail_file;
6599
6600 if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->no_light_thumbnail_file)) {
6601 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add no light thumbnail %1% from file %2% failed\n") % (i+1) %plate_data->no_light_thumbnail_file;
6602 return false;
6603 }
6604 }
6605
6606 if (!top_thumbnail_status[i] && !plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file))){
6607 std::string dst_in_3mf = (boost::format("Metadata/top_%1%.png") % (i + 1)).str();
6608
6609 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add top thumbnail %1% from file %2%") % (i+1) %plate_data->top_file;
6610 if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->top_file)) {
6611 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add top thumbnail %1% failed") % (i+1);
6612 return false;
6613 }
6614 top_thumbnail_status[i] = true;
6615 }
6616
6617 if (!pick_thumbnail_status[i] && !plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file))){
6618 std::string dst_in_3mf = (boost::format("Metadata/pick_%1%.png") % (i + 1)).str();
6619
6620 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add pick thumbnail %1% from file %2%") % (i+1) %plate_data->pick_file;
6621 if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->pick_file)) {
6622 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add pick thumbnail %1% failed") % (i+1);
6623 return false;
6624 }
6625 pick_thumbnail_status[i] = true;
6626 }
6627 }
6628 if (proFn) {
6629 proFn(EXPORT_STAGE_ADD_THUMBNAILS, plate_data_list.size(), plate_data_list.size(), cb_cancel);
6630 if (cb_cancel)
6631 return false;
6632 }
6633 }
6634
6635 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",before add calibration thumbnails, count %1%\n")%calibration_data.size();
6636 //BBS add calibration thumbnail for each plate
6637 if (!m_skip_static && calibration_data.size() > 0) {
6638 // Adds the file Metadata/calibration_p[X].png.
6639 for (unsigned int index = 0; index < calibration_data.size(); index++)
6640 {
6641 if (proFn) {
6642 proFn(EXPORT_STAGE_ADD_THUMBNAILS, index, calibration_data.size(), cb_cancel);
6643 if (cb_cancel)
6644 return false;
6645 }
6646
6647 if (calibration_data[index]->is_valid())
6648 {
6649 if (!_add_calibration_file_to_archive(archive, *calibration_data[index], index)) {
6650 close_zip_writer(&archive);
6651 return false;
6652 }
6653 }
6654 }
6655 }
6656
6657 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",before add calibration boundingbox, count %1%\n")%id_bboxes.size();
6658 if (!m_skip_static && id_bboxes.size() > 0) {
6659 // Adds the file Metadata/calibration_p[X].png.
6660 for (unsigned int index = 0; index < id_bboxes.size(); index++)
6661 {
6662 // BBS: save bounding box to json
6663 if (id_bboxes[index]->is_valid()) {
6664 if (!_add_bbox_file_to_archive(archive, *id_bboxes[index], index)) {
6665 close_zip_writer(&archive);
6666 return false;
6667 }
6668 }
6669 }
6670 }
6671
6672 //BBS progress point
6673 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add models\n");
6674 if (proFn) {
6675 proFn(EXPORT_STAGE_ADD_MODELS, 0, 1, cb_cancel);
6676 if (cb_cancel)
6677 return false;
6678 }
6679
6680 // Adds model file ("3D/3dmodel.model").
6681 // This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
6682 ObjectToObjectDataMap objects_data;
6683 //if (!m_skip_model)
6684 {
6685 if (!_add_model_file_to_archive(filename, archive, model, objects_data, proFn, project)) { return false; }
6686
6687 // Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
6688 // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
6689 // The index differes from the index of an object ID of an object instance of a 3MF file!
6690 if (!_add_layer_height_profile_file_to_archive(archive, model)) {
6691 close_zip_writer(&archive);
6692 return false;
6693 }
6694
6695 if (!_add_cad_recipe_file_to_archive(archive, model)) {
6696 close_zip_writer(&archive);
6697 return false;
6698 }
6699
6700 // BBS progress point
6701 /*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_LAYER_RANGE\n");
6702 if (proFn) {
6703 proFn(EXPORT_STAGE_ADD_LAYER_RANGE, 0, 1, cb_cancel);
6704 if (cb_cancel)
6705 return false;
6706 }*/
6707
6708 // Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
6709 // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
6710 // The index differes from the index of an object ID of an object instance of a 3MF file!
6711 if (!_add_layer_config_ranges_file_to_archive(archive, model)) {
6712 close_zip_writer(&archive);
6713 return false;
6714 }
6715
6716 if (!_add_brim_ear_points_file_to_archive(archive, model)) {
6717 close_zip_writer(&archive);
6718 return false;
6719 }
6720
6721 // BBS progress point
6722 /*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_SUPPORT\n");
6723 if (proFn) {
6724 proFn(EXPORT_STAGE_ADD_SUPPORT, 0, 1, cb_cancel);
6725 if (cb_cancel)
6726 return false;
6727 }
6728
6729 // Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
6730 // All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
6731 // The index differes from the index of an object ID of an object instance of a 3MF file!
6732 if (!_add_sla_support_points_file_to_archive(archive, model)) {
6733 return false;
6734 }
6735
6736 if (!_add_sla_drain_holes_file_to_archive(archive, model)) {
6737 return false;
6738 }*/
6739
6740 // BBS progress point
6741 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", before add custom gcodes\n");
6742 if (proFn) {
6743 proFn(EXPORT_STAGE_ADD_CUSTOM_GCODE, 0, 1, cb_cancel);
6744 if (cb_cancel) return false;
6745 }
6746
6747 // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
6748 // All custom gcode per height of whole Model are stored here
6749 if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config)) { return false; }
6750
6751 // BBS progress point
6752 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", before add project_settings\n");
6753 if (proFn) {
6754 proFn(EXPORT_STAGE_ADD_PRINT_CONFIG, 0, 1, cb_cancel);
6755 if (cb_cancel) return false;
6756 }
6757
6758 // Adds slic3r print config file ("Metadata/Slic3r_PE.config").
6759 // This file contains the content of FullPrintConfig / SLAFullPrintConfig.
6760 // Omitted for minimal published 3MF: OrcaSlicer versions without the publish feature
6761 // then fall back to importing the geometry only, and new versions read the published
6762 // payload from the model metadata instead.
6763 if (config != nullptr && !m_minimal_published) {
6764 // BBS: change to json format
6765 // if (!_add_print_config_file_to_archive(archive, *config)) {
6766 if (!_add_project_config_file_to_archive(archive, *config, model)) { return false; }
6767 }
6768
6769 // BBS progress point
6770 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", before add project embedded settings\n");
6771 if (proFn) {
6772 proFn(EXPORT_STAGE_ADD_CONFIG_FILE, 0, 1, cb_cancel);
6773 if (cb_cancel) return false;
6774 }
6775
6776 // BBS: add project config (omitted for minimal published 3MF)
6777 if (!m_minimal_published && project_presets.size() > 0) {
6778 // BBS: add project embedded preset files
6779 _add_project_embedded_presets_to_archive(archive, model, project_presets);
6780
6781 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", finished add project embedded settings, size %1%\n")%project_presets.size();
6782 if (proFn) {
6783 proFn(EXPORT_STAGE_ADD_PROJECT_CONFIG, 0, 1, cb_cancel);
6784 if (cb_cancel) return false;
6785 }
6786 }
6787 }
6788
6789 // add plate_N.gcode.md5 to file
6790 if (!m_skip_static && m_save_gcode) {
6791 for (int i = 0; i < plate_data_list.size(); i++) {
6792 PlateData *plate_data = plate_data_list[i];
6793 if (!plate_data->gcode_file.empty() && plate_data->is_sliced_valid && boost::filesystem::exists(plate_data->gcode_file)) {
6794 unsigned char digest[16];
6795 MD5_CTX ctx;
6796 MD5_Init(&ctx);
6797 auto src_gcode_file = plate_data->gcode_file;
6798 boost::filesystem::ifstream ifs(src_gcode_file, std::ios::binary);
6799 std::string buf(64 * 1024, 0);
6800 const std::size_t & size = boost::filesystem::file_size(src_gcode_file);
6801 std::size_t left_size = size;
6802 while (ifs) {
6803 ifs.read(buf.data(), buf.size());
6804 int read_bytes = ifs.gcount();
6805 MD5_Update(&ctx, (unsigned char *) buf.data(), read_bytes);
6806 }
6807 MD5_Final(digest, &ctx);
6808 char md5_str[33];
6809 for (int j = 0; j < 16; j++) { sprintf(&md5_str[j * 2], "%02X", (unsigned int) digest[j]); }
6810 plate_data->gcode_file_md5 = std::string(md5_str);
6811 std::string target_file = (boost::format("Metadata/plate_%1%.gcode.md5") % (plate_data->plate_index + 1)).str();
6812 if (!mz_zip_writer_add_mem(&archive, target_file.c_str(), (const void *) plate_data->gcode_file_md5.c_str(), plate_data->gcode_file_md5.length(),
6813 MZ_DEFAULT_COMPRESSION)) {
6814 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__
6815 << boost::format(", store gcode md5 to 3mf's %1%, length %2%, failed\n") %target_file %plate_data->gcode_file_md5.length();
6816 return false;
6817 }
6818 }
6819 }
6820 }
6821
6822 // Adds gcode files ("Metadata/plate_1.gcode, plate_2.gcode, ...)
6823 // Before _add_model_config_file_to_archive, because we modify plate_data
6824 //if (!m_skip_static && !_add_gcode_file_to_archive(archive, model, plate_data_list, proFn)) {
6825 if (!m_skip_static && m_save_gcode && !_add_gcode_file_to_archive(archive, model, plate_data_list, proFn)) {
6826 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_gcode_file_to_archive failed\n");
6827 return false;
6828 }
6829
6830 // Adds slic3r model config file ("Metadata/Slic3r_PE_model.config").
6831 // This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides).
6832 // As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data
6833 // is stored here as well.
6834 if (!_add_model_config_file_to_archive(archive, model, plate_data_list, objects_data, *config, export_plate_idx, m_save_gcode, m_use_loaded_id)) {
6835 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_model_config_file_to_archive failed\n");
6836 return false;
6837 }
6838
6839 if (!_add_cut_information_file_to_archive(archive, model)) {
6840 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_cut_information_file_to_archive failed\n");
6841 return false;
6842 }
6843
6844 //BBS progress point
6845 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add sliced info to 3mf\n");
6846 if (proFn) {
6847 proFn(EXPORT_STAGE_ADD_SLICE_INFO, 0, 1, cb_cancel);
6848 if (cb_cancel)
6849 return false;
6850 }
6851
6852 // Adds sliced info of plate file ("Metadata/slice_info.config")
6853 // This file contains all sliced info of all plates
6854 if (!_add_slice_info_config_file_to_archive(archive, model, plate_data_list, objects_data, *config)) {
6855 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_slice_info_config_file_to_archive failed\n");
6856 return false;
6857 }
6858
6859 if (!_add_filament_sequence_file_to_archive(archive, plate_data_list)) {
6860 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_filament_sequence_file_to_archive failed\n");
6861 return false;
6862 }
6863
6864 //BBS progress point
6865 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add auxiliary dir to 3mf\n");
6866 if (proFn) {
6867 proFn(EXPORT_STAGE_ADD_AUXILIARIES, 0, 1, cb_cancel);
6868 if (cb_cancel)
6869 return false;
6870 }
6871
6872 if (!m_skip_static && !_add_auxiliary_dir_to_archive(archive, model.get_auxiliary_file_temp_path(), temp_data)) {
6873 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", _add_auxiliary_dir_to_archive failed\n");
6874 return false;
6875 }
6876
6877 //BBS progress point
6878 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add relation file to 3mf\n");
6879 if (proFn) {
6880 proFn(EXPORT_STAGE_ADD_RELATIONS, 0, 1, cb_cancel);
6881 if (cb_cancel)
6882 return false;
6883 }
6884
6885 // Adds relationships file ("_rels/.rels").
6886 // The content of this file is the same for each OrcaSlicer 3mf.
6887 // The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
6888 if (!_add_relationships_file_to_archive(archive, {}, {}, {}, temp_data, export_plate_idx, rels_thumbnail_written, rels_small_thumbnail_written)) {
6889 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", _add_relationships_file_to_archive failed\n");
6890 return false;
6891 }
6892
6893 if (!mz_zip_writer_finalize_archive(&archive)) {
6894 add_error("Unable to finalize the archive");
6895 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to finalize the archive\n");
6896 return false;
6897 }
6898
6899 //BBS progress point
6900 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", finished exporting 3mf\n");
6901 if (proFn) {
6902 proFn(EXPORT_STAGE_FINISH, 0, 1, cb_cancel);
6903 if (cb_cancel)
6904 return false;
6905 }
6906
6907 lock.filename = nullptr;
6908
6909 return true;
6910 }
bool _add_calibration_file_to_archive(mz_zip_archive &archive, const ThumbnailData &thumbnail_data, int index)
Definition bbs_3mf.cpp:7025
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive &archive, Model &model, const DynamicPrintConfig *config)
Definition bbs_3mf.cpp:9079
bool _add_model_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig &config, int export_plate_idx=-1, bool save_gcode=true, bool use_loaded_id=false)
Definition bbs_3mf.cpp:8296
bool _add_project_embedded_presets_to_archive(mz_zip_archive &archive, Model &model, std::vector< Preset * > project_presets)
Definition bbs_3mf.cpp:8208
bool m_skip_static
Definition bbs_3mf.cpp:6223
bool _add_project_config_file_to_archive(mz_zip_archive &archive, const DynamicPrintConfig &config, Model &model)
Definition bbs_3mf.cpp:8199
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive &archive, Model &model)
Definition bbs_3mf.cpp:7994
bool _add_content_types_file_to_archive(mz_zip_archive &archive)
Definition bbs_3mf.cpp:6934
bool _add_brim_ear_points_file_to_archive(mz_zip_archive &archive, Model &model)
Definition bbs_3mf.cpp:8053
bool _add_layer_height_profile_file_to_archive(mz_zip_archive &archive, Model &model)
Definition bbs_3mf.cpp:7946
bool _add_thumbnail_file_to_archive(mz_zip_archive &archive, const ThumbnailData &thumbnail_data, const char *local_path, int index, bool generate_small_thumbnail=false)
Definition bbs_3mf.cpp:6959
bool _add_model_file_to_archive(const std::string &filename, mz_zip_archive &archive, const Model &model, ObjectToObjectDataMap &objects_data, Export3mfProgressFn proFn=nullptr, BBLProject *project=nullptr) const
Definition bbs_3mf.cpp:7159
std::map< ModelObject const *, ObjectData > ObjectToObjectDataMap
Definition bbs_3mf.cpp:6218
bool _add_gcode_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, Export3mfProgressFn proFn=nullptr)
Definition bbs_3mf.cpp:9005
bool m_save_gcode
Definition bbs_3mf.cpp:6226
bool _add_cad_recipe_file_to_archive(mz_zip_archive &archive, Model &model)
Definition bbs_3mf.cpp:7981
bool _add_filament_sequence_file_to_archive(mz_zip_archive &archive, const PlateDataPtrs &plate_data_list)
Definition bbs_3mf.cpp:8662
bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
Definition bbs_3mf.cpp:7883
bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig &config)
Definition bbs_3mf.cpp:8715
bool _add_auxiliary_dir_to_archive(mz_zip_archive &archive, const std::string &aux_dir, PackingTemporaryData &data)
Definition bbs_3mf.cpp:9136
bool _add_bbox_file_to_archive(mz_zip_archive &archive, const PlateBBoxData &id_bboxes, int index)
Definition bbs_3mf.cpp:7045
const int EXPORT_STAGE_ADD_THUMBNAILS
Definition bbs_3mf.hpp:245
bool close_zip_writer(mz_zip_archive *zip)
Definition miniz_extension.cpp:130
const int EXPORT_STAGE_ADD_CUSTOM_GCODE
Definition bbs_3mf.hpp:250
const int EXPORT_STAGE_OPEN_3MF
Definition bbs_3mf.hpp:243
const int EXPORT_STAGE_ADD_AUXILIARIES
Definition bbs_3mf.hpp:256
const int EXPORT_STAGE_ADD_PRINT_CONFIG
Definition bbs_3mf.hpp:251
bool open_zip_writer(mz_zip_archive *zip, const std::string &fname)
Definition miniz_extension.cpp:124
const int EXPORT_STAGE_CONTENT_TYPES
Definition bbs_3mf.hpp:244
const int EXPORT_STAGE_ADD_SLICE_INFO
Definition bbs_3mf.hpp:254
const int EXPORT_STAGE_FINISH
Definition bbs_3mf.hpp:257
const int EXPORT_STAGE_ADD_PROJECT_CONFIG
Definition bbs_3mf.hpp:252
const int EXPORT_STAGE_ADD_RELATIONS
Definition bbs_3mf.hpp:246
const int EXPORT_STAGE_ADD_CONFIG_FILE
Definition bbs_3mf.hpp:253
bool is_valid(const FontFile &font, unsigned int index)
Definition Emboss.cpp:284
@ lock
Definition GLGizmoEmboss.cpp:209

References _add_auxiliary_dir_to_archive(), _add_bbox_file_to_archive(), _add_brim_ear_points_file_to_archive(), _add_cad_recipe_file_to_archive(), _add_calibration_file_to_archive(), _add_content_types_file_to_archive(), _add_custom_gcode_per_print_z_file_to_archive(), _add_cut_information_file_to_archive(), _add_filament_sequence_file_to_archive(), _add_file_to_archive(), _add_gcode_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_project_config_file_to_archive(), _add_project_embedded_presets_to_archive(), _add_relationships_file_to_archive(), _add_slice_info_config_file_to_archive(), _add_thumbnail_file_to_archive(), _save_model_to_file(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::close_zip_writer(), Slic3r::EXPORT_STAGE_ADD_AUXILIARIES, Slic3r::EXPORT_STAGE_ADD_CONFIG_FILE, Slic3r::EXPORT_STAGE_ADD_CUSTOM_GCODE, Slic3r::EXPORT_STAGE_ADD_MODELS, Slic3r::EXPORT_STAGE_ADD_PRINT_CONFIG, Slic3r::EXPORT_STAGE_ADD_PROJECT_CONFIG, Slic3r::EXPORT_STAGE_ADD_RELATIONS, Slic3r::EXPORT_STAGE_ADD_SLICE_INFO, Slic3r::EXPORT_STAGE_ADD_THUMBNAILS, Slic3r::EXPORT_STAGE_CONTENT_TYPES, Slic3r::EXPORT_STAGE_FINISH, Slic3r::EXPORT_STAGE_OPEN_3MF, Slic3r::PlateData::gcode_file, Slic3r::PlateData::gcode_file_md5, Slic3r::Model::get_auxiliary_file_temp_path(), Slic3r::PlateData::is_sliced_valid, m_minimal_published, m_save_gcode, m_skip_static, m_use_loaded_id, Slic3r::PlateData::no_light_thumbnail_file, Slic3r::open_zip_writer(), Slic3r::PlateData::pick_file, Slic3r::PlateData::plate_index, Slic3r::PlateData::thumbnail_file, and Slic3r::PlateData::top_file.

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::_BBS_3MF_Base::add_error ( const std::string & error) const
inlineprotectedinherited
816{ boost::unique_lock l(mutex); m_errors.push_back(error); }
std::vector< std::string > m_errors
Definition bbs_3mf.cpp:813

References m_errors, and mutex.

Referenced by Slic3r::_BBS_3MF_Exporter::_add_bbox_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_brim_ear_points_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_cad_recipe_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_content_types_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_cut_information_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_filament_sequence_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_layer_config_ranges_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_mesh_to_object_stream(), Slic3r::_BBS_3MF_Exporter::_add_model_config_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_model_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_object_to_model_stream(), Slic3r::_BBS_3MF_Exporter::_add_print_config_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_relationships_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_thumbnail_file_to_archive(), Slic3r::_BBS_3MF_Importer::_apply_texture_displacement(), Slic3r::_BBS_3MF_Importer::_create_object_instance(), Slic3r::_BBS_3MF_Importer::_extract_auxiliary_file_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_brim_ear_points_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_cut_information_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_embossed_svg_shape_file(), Slic3r::_BBS_3MF_Importer::_extract_filament_sequence_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_file_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_layer_config_ranges_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_layer_heights_profile_config_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_model_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_print_config_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_project_config_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_project_embedded_presets_from_archive(), Slic3r::_BBS_3MF_Importer::_extract_xml_from_archive(), Slic3r::_BBS_3MF_Importer::_generate_current_object_list(), Slic3r::_BBS_3MF_Importer::_generate_volumes_new(), Slic3r::_BBS_3MF_Importer::_handle_end_config_plater(), Slic3r::_BBS_3MF_Importer::_handle_end_config_plater_instance(), Slic3r::_BBS_3MF_Importer::_handle_end_model(), Slic3r::_BBS_3MF_Importer::_handle_end_object(), Slic3r::_BBS_3MF_Importer::_handle_start_assemble_item(), Slic3r::_BBS_3MF_Importer::_handle_start_config_metadata(), Slic3r::_BBS_3MF_Importer::_handle_start_config_object(), Slic3r::_BBS_3MF_Importer::_handle_start_config_plater_instance(), Slic3r::_BBS_3MF_Importer::_handle_start_config_volume(), Slic3r::_BBS_3MF_Importer::_handle_start_config_volume_mesh(), Slic3r::_BBS_3MF_Importer::_handle_start_shape_configuration(), Slic3r::_BBS_3MF_Importer::_handle_start_text_configuration(), Slic3r::_BBS_3MF_Importer::_handle_start_text_info_item(), Slic3r::_BBS_3MF_Importer::_load_model_from_file(), Slic3r::_BBS_3MF_Exporter::_save_model_to_file(), Slic3r::_BBS_3MF_Importer::get_thumbnail(), Slic3r::_BBS_3MF_Importer::load_gcode_3mf_from_stream(), Slic3r::_BBS_3MF_Exporter::save_model_to_file(), and Slic3r::_BBS_3MF_Exporter::save_object_mesh().

◆ add_transformation()

void Slic3r::_BBS_3MF_Exporter::add_transformation ( std::stringstream & stream,
const Transform3d & tr )
static
7845 {
7846 for (unsigned c = 0; c < 4; ++c) {
7847 for (unsigned r = 0; r < 3; ++r) {
7848 stream << tr(r, c);
7849 if (r != 2 || c != 3) stream << " ";
7850 }
7851 }
7852 }

References add_transformation().

Referenced by _add_build_to_model_stream(), add_transformation(), and Slic3r::to_xml().

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

◆ clear_errors()

void Slic3r::_BBS_3MF_Base::clear_errors ( )
inlineprotectedinherited
817{ m_errors.clear(); }

References m_errors.

Referenced by Slic3r::_BBS_3MF_Importer::~_BBS_3MF_Importer(), Slic3r::_BBS_3MF_Importer::load_model_from_file(), and Slic3r::_BBS_3MF_Exporter::save_model_to_file().

Here is the caller graph for this function:

◆ convert_instance_id_to_resource_id()

int Slic3r::_BBS_3MF_Exporter::convert_instance_id_to_resource_id ( const Model & model,
int obj_id,
int instance_id )
inlinestaticprivate
6304 {
6305 int resource_id = 1;
6306
6307 for (int i = 0; i < obj_id; ++i)
6308 {
6309 resource_id += model.objects[i]->volumes.size() + 1;
6310 }
6311
6312 resource_id += model.objects[obj_id]->volumes.size();
6313
6314 return resource_id;
6315 }

References Slic3r::Model::objects.

◆ log_errors()

void Slic3r::_BBS_3MF_Base::log_errors ( )
inlineinherited
821 {
822 for (const std::string& error : m_errors)
823 BOOST_LOG_TRIVIAL(error) << error;
824 }

References m_errors.

Referenced by Slic3r::bbs_3mf_get_thumbnail(), Slic3r::load_bbs_3mf(), Slic3r::load_gcode_3mf_from_stream(), and Slic3r::store_bbs_3mf().

Here is the caller graph for this function:

◆ save_model_to_file()

bool Slic3r::_BBS_3MF_Exporter::save_model_to_file ( StoreParams & store_params)
6319 {
6320 clear_errors();
6321 m_fullpath_sources = store_params.strategy & SaveStrategy::FullPathSources;
6322 m_zip64 = store_params.strategy & SaveStrategy::Zip64;
6323 m_production_ext = store_params.strategy & SaveStrategy::ProductionExt;
6324
6325 m_skip_static = store_params.strategy & SaveStrategy::SkipStatic;
6326 m_split_model = store_params.strategy & SaveStrategy::SplitModel;
6327 m_save_gcode = store_params.strategy & SaveStrategy::WithGcode;
6328 m_skip_model = store_params.strategy & SaveStrategy::SkipModel;
6329 m_skip_auxiliary = store_params.strategy & SaveStrategy::SkipAuxiliary;
6330 m_share_mesh = store_params.strategy & SaveStrategy::ShareMesh;
6331 m_from_backup_save = store_params.strategy & SaveStrategy::Backup;
6332 m_minimal_published = store_params.strategy & SaveStrategy::MinimalPublished;
6333
6334 m_use_loaded_id = store_params.strategy & SaveStrategy::UseLoadedId;
6335
6336 if (auto info = store_params.model->model_info) {
6337 if (auto iter = info->metadata_items.find("Thumbnail_Small"); iter != info->metadata_items.end())
6338 m_thumbnail_small = iter->second;
6339 if (auto iter = info->metadata_items.find("Thumbnail_Middle"); iter != info->metadata_items.end())
6340 m_thumbnail_middle = iter->second;
6341 }
6342 boost::system::error_code ec;
6343 std::string filename = store_params.path;
6344 boost::filesystem::remove(filename + ".tmp", ec);
6345
6346 bool result = _save_model_to_file(filename + ".tmp", *store_params.model, store_params.plate_data_list, store_params.project_presets, store_params.config,
6347 store_params.thumbnail_data, store_params.no_light_thumbnail_data, store_params.top_thumbnail_data, store_params.pick_thumbnail_data,
6348 store_params.proFn,
6349 store_params.calibration_thumbnail_data, store_params.id_bboxes, store_params.project, store_params.export_plate_idx);
6350 if (result) {
6351 boost::filesystem::rename(filename + ".tmp", filename, ec);
6352 if (ec) {
6353 add_error("Failed to rename file: " + ec.message());
6354 boost::filesystem::remove(filename + ".tmp", ec);
6355 return false;
6356 }
6357 if (!(store_params.strategy & SaveStrategy::Silence))
6358 save_string_file(store_params.model->get_backup_path() + "/origin.txt", filename);
6359 }
6360 return result;
6361 }
void clear_errors()
Definition bbs_3mf.cpp:817
bool _save_model_to_file(const std::string &filename, Model &model, PlateDataPtrs &plate_data_list, std::vector< Preset * > &project_presets, const DynamicPrintConfig *config, const std::vector< ThumbnailData * > &thumbnail_data, const std::vector< ThumbnailData * > &no_light_thumbnail_data, const std::vector< ThumbnailData * > &top_thumbnail_data, const std::vector< ThumbnailData * > &pick_thumbnail_data, Export3mfProgressFn proFn, const std::vector< ThumbnailData * > &calibration_data, const std::vector< PlateBBoxData * > &id_bboxes, BBLProject *project=nullptr, int export_plate_idx=-1)
Definition bbs_3mf.cpp:6418
void save_string_file(const boost::filesystem::path &p, const std::string &str)
Definition utils.cpp:2156
@ WithGcode
Definition bbs_3mf.hpp:169
@ Backup
Definition bbs_3mf.hpp:185
@ SkipStatic
Definition bbs_3mf.hpp:171
@ SkipAuxiliary
Definition bbs_3mf.hpp:174
@ ShareMesh
Definition bbs_3mf.hpp:176
@ MinimalPublished
Definition bbs_3mf.hpp:179
@ Silence
Definition bbs_3mf.hpp:170
@ SkipModel
Definition bbs_3mf.hpp:172
@ FullPathSources
Definition bbs_3mf.hpp:165
@ UseLoadedId
Definition bbs_3mf.hpp:175
@ ProductionExt
Definition bbs_3mf.hpp:167
@ Zip64
Definition bbs_3mf.hpp:166
@ SplitModel
Definition bbs_3mf.hpp:183

References _save_model_to_file(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::Backup, Slic3r::StoreParams::calibration_thumbnail_data, Slic3r::_BBS_3MF_Base::clear_errors(), Slic3r::StoreParams::config, Slic3r::StoreParams::export_plate_idx, Slic3r::FullPathSources, Slic3r::Model::get_backup_path(), Slic3r::StoreParams::id_bboxes, m_from_backup_save, m_fullpath_sources, m_minimal_published, m_production_ext, m_save_gcode, m_share_mesh, m_skip_auxiliary, m_skip_model, m_skip_static, m_split_model, m_thumbnail_middle, m_thumbnail_small, m_use_loaded_id, m_zip64, Slic3r::MinimalPublished, Slic3r::StoreParams::model, Slic3r::Model::model_info, Slic3r::StoreParams::no_light_thumbnail_data, Slic3r::StoreParams::path, Slic3r::StoreParams::pick_thumbnail_data, Slic3r::StoreParams::plate_data_list, Slic3r::ProductionExt, Slic3r::StoreParams::proFn, Slic3r::StoreParams::project, Slic3r::StoreParams::project_presets, save_model_to_file(), Slic3r::save_string_file(), Slic3r::ShareMesh, Slic3r::Silence, Slic3r::SkipAuxiliary, Slic3r::SkipModel, Slic3r::SkipStatic, Slic3r::SplitModel, Slic3r::StoreParams::strategy, Slic3r::StoreParams::thumbnail_data, Slic3r::StoreParams::top_thumbnail_data, Slic3r::UseLoadedId, Slic3r::WithGcode, and Slic3r::Zip64.

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

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

◆ save_object_mesh()

bool Slic3r::_BBS_3MF_Exporter::save_object_mesh ( const std::string & temp_path,
ModelObject const & object,
int obj_id )
6365 {
6366 m_production_ext = true;
6367 m_from_backup_save = true;
6368 Model const & model = *object.get_model();
6369
6370 mz_zip_archive archive;
6371 mz_zip_zero_struct(&archive);
6372
6373 auto filename = boost::format("3D/Objects/%s_%d.model") % object.name % obj_id;
6374 std::string filepath = temp_path + "/" + filename.str();
6375 std::string filepath_tmp = filepath + ".tmp";
6376 boost::system::error_code ec;
6377 boost::filesystem::remove(filepath_tmp, ec);
6378 if (!open_zip_writer(&archive, filepath_tmp)) {
6379 add_error("Unable to open the file"+filepath_tmp);
6380 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n");
6381 return false;
6382 }
6383
6384 struct close_lock
6385 {
6386 mz_zip_archive & archive;
6387 std::string const * filename;
6388 void close() {
6389 close_zip_writer(&archive);
6390 filename = nullptr;
6391 }
6392 ~close_lock() {
6393 if (filename) {
6394 close_zip_writer(&archive);
6395 boost::system::error_code ec;
6396 boost::filesystem::remove(*filename, ec);
6397 }
6398 }
6399 } lock{archive, &filepath_tmp};
6400
6401 ObjectToObjectDataMap objects_data;
6402 auto & volumes_objectID = objects_data.insert({&object, {&object, obj_id}}).first->second.volumes_objectID;
6403 unsigned int volume_count = 0;
6404 for (ModelVolume *volume : object.volumes) {
6405 if (volume == nullptr) continue;
6406 volumes_objectID.insert({volume, (++volume_count << 16 | obj_id)});
6407 }
6408
6409 _add_model_file_to_archive(filename.str(), archive, model, objects_data);
6410
6411 mz_zip_writer_finalize_archive(&archive);
6412 lock.close();
6413 boost::filesystem::rename(filepath_tmp, filepath, ec);
6414 return true;
6415 }

References _add_model_file_to_archive(), Slic3r::_BBS_3MF_Base::add_error(), Slic3r::close_zip_writer(), m_from_backup_save, m_production_ext, Slic3r::open_zip_writer(), and save_object_mesh().

Referenced by Slic3r::_BBS_Backup_Manager::process_task(), and save_object_mesh().

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::_BBS_3MF_Base::m_errors
mutableprivateinherited

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

◆ m_from_backup_save

bool Slic3r::_BBS_3MF_Exporter::m_from_backup_save { false }
private
6224{ false }; // the object save is from backup store

Referenced by _add_model_file_to_archive(), save_model_to_file(), and save_object_mesh().

◆ m_fullpath_sources

bool Slic3r::_BBS_3MF_Exporter::m_fullpath_sources { false }
private
6220{ false };

Referenced by _add_model_config_file_to_archive(), and save_model_to_file().

◆ m_minimal_published

bool Slic3r::_BBS_3MF_Exporter::m_minimal_published { false }
private
6229{ false }; // published 3MF: omit the project config, the embedded preset files and the slicer tags

Referenced by _add_model_file_to_archive(), _save_model_to_file(), and save_model_to_file().

◆ m_production_ext

bool Slic3r::_BBS_3MF_Exporter::m_production_ext { false }
private

◆ m_save_gcode

bool Slic3r::_BBS_3MF_Exporter::m_save_gcode { false }
private
6226{ false }; // whether to save gcode for normal save

Referenced by _save_model_to_file(), and save_model_to_file().

◆ m_share_mesh

bool Slic3r::_BBS_3MF_Exporter::m_share_mesh { false }
private
6231{ false }; // whether to share mesh between objects

Referenced by _add_mesh_to_object_stream(), _add_model_config_file_to_archive(), _add_model_file_to_archive(), and save_model_to_file().

◆ m_shared_meshes

std::map<void const *, std::pair<ObjectData*, ModelVolume const *> > Slic3r::_BBS_3MF_Exporter::m_shared_meshes
private

◆ m_skip_auxiliary

bool Slic3r::_BBS_3MF_Exporter::m_skip_auxiliary { false }
private
6228{ false }; // skip normal axuiliary files

Referenced by _add_auxiliary_dir_to_archive(), and save_model_to_file().

◆ m_skip_model

bool Slic3r::_BBS_3MF_Exporter::m_skip_model { false }
private
6227{ false }; // skip model when exporting .gcode.3mf

Referenced by _add_model_config_file_to_archive(), _add_model_file_to_archive(), and save_model_to_file().

◆ m_skip_static

bool Slic3r::_BBS_3MF_Exporter::m_skip_static { false }
private
6223{ false }; // not save mesh and other big static contents

Referenced by _save_model_to_file(), and save_model_to_file().

◆ m_split_model

bool Slic3r::_BBS_3MF_Exporter::m_split_model { false }
private
6225{ false }; // save object per file with Production Extention

Referenced by _add_model_file_to_archive(), and save_model_to_file().

◆ m_thumbnail_middle

std::string Slic3r::_BBS_3MF_Exporter::m_thumbnail_middle = PRINTER_THUMBNAIL_MIDDLE_FILE
private

◆ m_thumbnail_small

std::string Slic3r::_BBS_3MF_Exporter::m_thumbnail_small = PRINTER_THUMBNAIL_SMALL_FILE
private

◆ m_use_loaded_id

bool Slic3r::_BBS_3MF_Exporter::m_use_loaded_id { false }
private
6230{ false }; // whether to use loaded id for identify_id

Referenced by _add_slice_info_config_file_to_archive(), _save_model_to_file(), and save_model_to_file().

◆ m_volume_paths

std::map<ModelVolume const *, std::pair<std::string, int> > Slic3r::_BBS_3MF_Exporter::m_volume_paths
private

◆ m_zip64

bool Slic3r::_BBS_3MF_Exporter::m_zip64 { true }
private
6221{ true };

Referenced by _add_model_file_to_archive(), and save_model_to_file().

◆ mutex

boost::mutex Slic3r::_BBS_3MF_Base::mutex
mutableprivateinherited

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