diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 47a8e5280..acc94af76 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -45,6 +45,7 @@ const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config"; const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt"; const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml"; const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; +const std::string CUSTOM_GCODE_PER_HEIGHT_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_height.xml"; const char* MODEL_TAG = "model"; const char* RESOURCES_TAG = "resources"; @@ -411,6 +412,8 @@ namespace Slic3r { void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_custom_gcode_per_height_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename); bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); @@ -620,6 +623,11 @@ namespace Slic3r { // extract slic3r print config file _extract_print_config_from_archive(archive, stat, config, filename); } + if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_HEIGHT_FILE)) + { + // extract slic3r layer config ranges file + _extract_custom_gcode_per_height_from_archive(archive, stat); + } else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) { // extract slic3r model config file @@ -1050,6 +1058,43 @@ namespace Slic3r { return true; } + void _3MF_Importer::_extract_custom_gcode_per_height_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat) + { + if (stat.m_uncomp_size > 0) + { + std::string buffer((size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) { + add_error("Error while reading custom Gcodes per height data to buffer"); + return; + } + + std::istringstream iss(buffer); // wrap returned xml to istringstream + pt::ptree main_tree; + pt::read_xml(iss, main_tree); + + if (main_tree.front().first != "custom_gcodes_per_height") + return; + pt::ptree code_tree = main_tree.front().second; + + if (!m_model->custom_gcode_per_height.empty()) + m_model->custom_gcode_per_height.clear(); + + for (const auto& code : code_tree) + { + if (code.first != "code") + continue; + pt::ptree tree = code.second; + double height = tree.get(".height"); + std::string gcode = tree.get(".gcode"); + int extruder = tree.get(".extruder"); + std::string color = tree.get(".color"); + + m_model->custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)) ; + } + } + } + void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes) { if (m_xml_parser == nullptr) @@ -1821,6 +1866,7 @@ namespace Slic3r { bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data); + bool _add_custom_gcode_per_height_file_to_archive(mz_zip_archive& archive, Model& model); }; bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) @@ -1898,6 +1944,15 @@ namespace Slic3r { return false; } + // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_height.xml"). + // All custom gcode per height of whole Model are stored here + if (!_add_custom_gcode_per_height_file_to_archive(archive, model)) + { + close_zip_writer(&archive); + boost::filesystem::remove(filename); + return false; + } + // Adds slic3r print config file ("Metadata/Slic3r_PE.config"). // This file contains the content of FullPrintConfing / SLAFullPrintConfig. if (config != nullptr) @@ -2256,7 +2311,7 @@ namespace Slic3r { if (!tree.empty()) { std::ostringstream oss; - boost::property_tree::write_xml(oss, tree); + pt::write_xml(oss, tree); out = oss.str(); // Post processing("beautification") of the output string for a better preview @@ -2442,7 +2497,49 @@ namespace Slic3r { return true; } - bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version) +bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive& archive, Model& model) +{ + std::string out = ""; + + if (!model.custom_gcode_per_height.empty()) + { + pt::ptree tree; + pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); + + for (const Model::CustomGCode& code : model.custom_gcode_per_height) + { + pt::ptree& code_tree = main_tree.add("code", ""); + // store minX and maxZ + code_tree.put(".height" , code.height ); + code_tree.put(".gcode" , code.gcode ); + code_tree.put(".extruder" , code.extruder ); + code_tree.put(".color" , code.color ); + } + + if (!tree.empty()) + { + std::ostringstream oss; + boost::property_tree::write_xml(oss, tree); + out = oss.str(); + + // Post processing("beautification") of the output string + boost::replace_all(out, "><", ">\n<"); + } + } + + if (!out.empty()) + { + if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_HEIGHT_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) + { + add_error("Unable to add custom Gcodes per height file to archive"); + return false; + } + } + + return true; +} + +bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version) { if ((path == nullptr) || (config == nullptr) || (model == nullptr)) return false; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 181d6cb99..213952df3 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -16,6 +16,10 @@ #include "AMF.hpp" +#include +#include +namespace pt = boost::property_tree; + #include #include #include @@ -147,6 +151,8 @@ struct AMFParserContext NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz + NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height + NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code NODE_TYPE_METADATA, // anywhere under amf/*/metadata }; @@ -227,7 +233,7 @@ struct AMFParserContext // Current instance allocated for an amf/constellation/instance subtree. Instance *m_instance; // Generic string buffer for vertices, face indices, metadata etc. - std::string m_value[3]; + std::string m_value[4]; // Pointer to config to update if config data are stored inside the amf file DynamicPrintConfig *m_config; @@ -268,6 +274,8 @@ void AMFParserContext::startElement(const char *name, const char **atts) } } else if (strcmp(name, "constellation") == 0) { node_type_new = NODE_TYPE_CONSTELLATION; + } else if (strcmp(name, "custom_gcodes_per_height") == 0) { + node_type_new = NODE_TYPE_CUSTOM_GCODE; } break; case 2: @@ -294,6 +302,13 @@ void AMFParserContext::startElement(const char *name, const char **atts) } else this->stop(); + } + else if (strcmp(name, "code") == 0 && m_path[1] == NODE_TYPE_CUSTOM_GCODE) { + node_type_new = NODE_TYPE_GCODE_PER_HEIGHT; + m_value[0] = get_attribute(atts, "height"); + m_value[1] = get_attribute(atts, "gcode"); + m_value[2] = get_attribute(atts, "extruder"); + m_value[3] = get_attribute(atts, "color"); } break; case 3: @@ -616,6 +631,19 @@ void AMFParserContext::endElement(const char * /* name */) m_instance = nullptr; break; + case NODE_TYPE_GCODE_PER_HEIGHT: { + double height = double(atof(m_value[0].c_str())); + const std::string& gcode = m_value[1]; + int extruder = atoi(m_value[2].c_str()); + const std::string& color = m_value[3]; + + m_model.custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)); + + for (std::string& val: m_value) + val.clear(); + break; + } + case NODE_TYPE_METADATA: if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) m_config->load_from_gcode_string(m_value[1].c_str()); @@ -1190,6 +1218,42 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << instances; stream << " \n"; } + + if (!model->custom_gcode_per_height.empty()) + { + std::string out = ""; + pt::ptree tree; + + pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); + + for (const Model::CustomGCode& code : model->custom_gcode_per_height) + { + pt::ptree& code_tree = main_tree.add("code", ""); + // store minX and maxZ + code_tree.put(".height", code.height); + code_tree.put(".gcode", code.gcode); + code_tree.put(".extruder", code.extruder); + code_tree.put(".color", code.color); + } + + if (!tree.empty()) + { + std::ostringstream oss; + pt::write_xml(oss, tree); + out = oss.str(); + + int del_header_pos = out.find("\n <", ">\n<"); + + stream << out << "\n"; + } + } + stream << "\n"; std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(export_path).filename().string(), ".zip.amf", ".amf"); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 861ad9dc1..ce3debfb5 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -41,6 +41,9 @@ Model& Model::assign_copy(const Model &rhs) mo->set_model(this); this->objects.emplace_back(mo); } + + // copy custom code per height + this->custom_gcode_per_height = rhs.custom_gcode_per_height; return *this; } @@ -59,6 +62,9 @@ Model& Model::assign_copy(Model &&rhs) for (ModelObject *model_object : this->objects) model_object->set_model(this); rhs.objects.clear(); + + // copy custom code per height + this->custom_gcode_per_height = rhs.custom_gcode_per_height; return *this; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f64fd8b43..6f1610d72 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2270,6 +2270,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ // and place the loaded config over the base. config += std::move(config_loaded); } + + this->model.custom_gcode_per_height = model.custom_gcode_per_height; } if (load_config)