From 9c0cb3b47cf84210c03b2c1e4e58c82112a01484 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 4 Jan 2024 21:18:04 +0100 Subject: [PATCH] printrequest implementation: printrequest reader WIP Co-authored-by: David Kocik transformation matrix loading Loading filaments from printRequest Conversion of string to double Reading PrintRequest when material not installed empty tree check + allow printrequest for mac PrintRequest: Set print profile if current doesnt have compatible sla material followup fix of c24477cd10d92de00874504be434872d49a20ec9 Fix compile error on gcc --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Format/PrintRequest.cpp | 161 ++++++++++++++++++++++++++ src/libslic3r/Format/PrintRequest.hpp | 12 ++ src/libslic3r/Model.cpp | 10 +- src/libslic3r/Preset.cpp | 18 ++- src/libslic3r/Preset.hpp | 4 +- src/libslic3r/PresetBundle.cpp | 14 +++ src/libslic3r/PresetBundle.hpp | 1 + src/slic3r/GUI/Plater.cpp | 59 +++++++++- 9 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 src/libslic3r/Format/PrintRequest.cpp create mode 100644 src/libslic3r/Format/PrintRequest.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 1ec0e1c8a0..6bf7035ec3 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -149,6 +149,8 @@ set(SLIC3R_SOURCES Format/SVG.cpp Format/SLAArchiveFormatRegistry.hpp Format/SLAArchiveFormatRegistry.cpp + Format/PrintRequest.cpp + Format/PrintRequest.cpp GCode/ThumbnailData.cpp GCode/ThumbnailData.hpp GCode/Thumbnails.cpp diff --git a/src/libslic3r/Format/PrintRequest.cpp b/src/libslic3r/Format/PrintRequest.cpp new file mode 100644 index 0000000000..c7a0a53fb1 --- /dev/null +++ b/src/libslic3r/Format/PrintRequest.cpp @@ -0,0 +1,161 @@ +#include "PrintRequest.hpp" + +#include +#include +#include +#include +#include +#include + +#include "libslic3r/Exception.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/Format/STL.hpp" + +//#include "slic3r/GUI/format.hpp" + +namespace Slic3r { +namespace pt = boost::property_tree; +namespace { +void read_file(const char* input_file, pt::ptree& tree) +{ + boost::filesystem::path path(input_file); + + boost::nowide::ifstream ifs(path.string()); + try { + pt::read_xml(ifs, tree); + } + catch (const boost::property_tree::xml_parser::xml_parser_error& err) { + throw Slic3r::RuntimeError("Failed reading PrintRequest file. File format is corrupt."); + } +} + +void read_tree(const boost::property_tree::ptree::value_type& section, boost::filesystem::path& model_path, std::string& material, std::string& material_color, std::vector& transformation_matrix) +{ + for (const auto& data : section.second) { + if (data.first == "Path") { + model_path = boost::filesystem::path(data.second.data()); + } + else if (data.first == "Material") { + material = data.second.data(); + } + else if (data.first == "MaterialColor") { + material_color = data.second.data(); + } + else if (data.first == "TransformationMatrix") { + transformation_matrix.reserve(16); + for (const auto& element : data.second) { + transformation_matrix.emplace_back(element.second.data()); + } + } + } +} +bool fill_model(Model* model, const boost::filesystem::path& model_path, const std::string& material, const std::vector& transformation_matrix) +{ + if (!boost::filesystem::exists(model_path)) + throw Slic3r::RuntimeError("Failed reading PrintRequest file. Path doesn't exists. " + model_path.string()); + if (!boost::algorithm::iends_with(model_path.string(), ".stl")) + throw Slic3r::RuntimeError("Failed reading PrintRequest file. Path is not stl file. " + model_path.string()); + bool result = load_stl(model_path.string().c_str(), model); + if (!material.empty()) { + model->objects.back()->volumes.front()->set_material_id(material); + } + return result; +} +void add_instance(Model* model, const boost::filesystem::path& model_path, const std::vector& transformation_matrix) +{ + if (transformation_matrix.size() >= 16) { + + auto string_to_double = [model_path](const std::string& from) -> double { + double ret_val; + auto answer = fast_float::from_chars(from.data(), from.data() + from.size(), ret_val); + if (answer.ec != std::errc()) + throw Slic3r::RuntimeError("Failed reading PrintRequest file. Couldn't parse transformation matrix. " + model_path.string()); + return ret_val; + }; + + Vec3d offset_vector; + Slic3r::Transform3d matrix; + try + { + offset_vector = Slic3r::Vec3d(string_to_double(transformation_matrix[3]), string_to_double(transformation_matrix[7]), string_to_double(transformation_matrix[11])); + // PrintRequest is row-major 4x4, Slic3r::Transform3d (Eigen) is column-major by default 3x3 + matrix(0, 0) = string_to_double(transformation_matrix[0]); + matrix(1, 0) = string_to_double(transformation_matrix[1]); + matrix(2, 0) = string_to_double(transformation_matrix[2]); + matrix(0, 1) = string_to_double(transformation_matrix[4]); + matrix(1, 1) = string_to_double(transformation_matrix[5]); + matrix(2, 1) = string_to_double(transformation_matrix[6]); + matrix(0, 2) = string_to_double(transformation_matrix[8]); + matrix(1, 2) = string_to_double(transformation_matrix[9]); + matrix(2, 2) = string_to_double(transformation_matrix[10]); + } + catch (const Slic3r::RuntimeError& e) { + throw e; + } + + + ModelObject* object = model->objects.back(); + Slic3r::Geometry::Transformation transformation(matrix); + transformation.set_offset(offset_vector); + object->add_instance(transformation); + } +} + +} + +bool load_printRequest(const char* input_file, Model* model) +{ + pt::ptree tree; + try + { + read_file(input_file, tree); + } + catch (const std::exception& e) + { + throw e; + } + + bool result = true; + + for (const auto& section0 : tree) { + if (section0.first != "PrintRequest") + continue; + if (section0.second.empty()) + continue; + for (const auto& section1 : section0.second) { + if (section1.first != "Files") + continue; + if (section1.second.empty()) + continue; + for (const auto& section2 : section1.second) { + if (section2.first != "File") + continue; + if (section2.second.empty()) + continue; + boost::filesystem::path model_path; + std::string material; + std::string material_color; + std::vector transformation_matrix; + + try + { + read_tree(section2, model_path, material, material_color, transformation_matrix); + result = result && fill_model(model, model_path, material, transformation_matrix); + if (!result) + return false; + add_instance(model, model_path, transformation_matrix); + } + catch (const std::exception& e) + { + throw e; + } + + + } + } + } + + return true; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Format/PrintRequest.hpp b/src/libslic3r/Format/PrintRequest.hpp new file mode 100644 index 0000000000..bf92d20482 --- /dev/null +++ b/src/libslic3r/Format/PrintRequest.hpp @@ -0,0 +1,12 @@ +#ifndef slic3r_Format_PrintRequest_hpp_ +#define slic3r_Format_PrintRequest_hpp_ + + + +namespace Slic3r { +class Model; +bool load_printRequest(const char* input_file, Model* model); + +} //namespace Slic3r + +#endif \ No newline at end of file diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2de86a5283..ba3e1075b9 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -28,6 +28,7 @@ #include "Format/3mf.hpp" #include "Format/STEP.hpp" #include "Format/SVG.hpp" +#include "Format/PrintRequest.hpp" #include @@ -140,6 +141,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false); else if (boost::algorithm::iends_with(input_file, ".svg")) result = load_svg(input_file, model); + else if (boost::ends_with(input_file, ".printRequest")) + result = load_printRequest(input_file.c_str(), &model); else throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .step/.stp, .svg, .amf(.xml) or extension .3mf(.zip)."); @@ -148,9 +151,10 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c if (model.objects.empty()) throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty"); - - for (ModelObject *o : model.objects) - o->input_file = input_file; + + if (!boost::ends_with(input_file, ".printRequest")) + for (ModelObject *o : model.objects) + o->input_file = input_file; if (options & LoadAttribute::AddDefaultInstances) model.add_default_instances(); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index ea1024101b..5c88a7a903 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1213,6 +1213,20 @@ const std::string& PresetCollection::get_preset_name_by_alias(const std::string& return alias; } +const std::string& PresetCollection::get_preset_name_by_alias_invisible(const std::string& alias) const +{ + for ( + // Find the 1st profile name with the alias. + auto it = Slic3r::lower_bound_by_predicate(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [&alias](auto& l) { return l.first < alias; }); + // Continue over all profile names with the same alias. + it != m_map_alias_to_profile_name.end() && it->first == alias; ++it) + if (auto it_preset = this->find_preset_internal(it->second); + it_preset != m_presets.end() && it_preset->name == it->second && + it_preset->is_compatible) + return it_preset->name; + return alias; +} + const std::string* PresetCollection::get_preset_name_renamed(const std::string &old_name) const { auto it_renamed = m_map_system_profile_renamed.find(old_name); @@ -1478,13 +1492,13 @@ Preset& PresetCollection::select_preset(size_t idx) return m_presets[idx]; } -bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force) +bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force, bool force_invisible /*= false*/) { std::string name = Preset::remove_suffix_modified(name_w_suffix); // 1) Try to find the preset by its name. auto it = this->find_preset_internal(name); size_t idx = 0; - if (it != m_presets.end() && it->name == name && it->is_visible) + if (it != m_presets.end() && it->name == name && (force_invisible || it->is_visible)) // Preset found by its name and it is visible. idx = it - m_presets.begin(); else { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index e3df0c24ba..51133eeb7b 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -410,6 +410,7 @@ public: PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); } const std::string& get_preset_name_by_alias(const std::string& alias) const; + const std::string& get_preset_name_by_alias_invisible(const std::string& alias) const; const std::string* get_preset_name_renamed(const std::string &old_name) const; // used to update preset_choice from Tab @@ -528,7 +529,8 @@ public: // Select a profile by its name. Return true if the selection changed. // Without force, the selection is only updated if the index changes. // With force, the changes are reverted if the new index is the same as the old index. - bool select_preset_by_name(const std::string &name, bool force); + // With force_invisible, force preset selection even it's invisible. + bool select_preset_by_name(const std::string &name, bool force, bool force_invisible = false); // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string path_from_name(const std::string &new_name) const; diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index f6399dc63f..5198c31c80 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -496,6 +496,20 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p return presets.get_preset_name_by_alias(alias); } +const std::string& PresetBundle::get_preset_name_by_alias_invisible(const Preset::Type& preset_type, const std::string& alias) const +{ + // there are not aliases for Printers profiles + if (preset_type == Preset::TYPE_PRINTER || preset_type == Preset::TYPE_INVALID) + return alias; + + const PresetCollection& presets = preset_type == Preset::TYPE_PRINT ? prints : + preset_type == Preset::TYPE_SLA_PRINT ? sla_prints : + preset_type == Preset::TYPE_FILAMENT ? filaments : + sla_materials; + + return presets.get_preset_name_by_alias_invisible(alias); +} + void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector& unselected_options) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index d7d56e8256..4bdc10ea4d 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -182,6 +182,7 @@ public: void load_installed_printers(const AppConfig &config); const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias, int extruder_id = -1); + const std::string& get_preset_name_by_alias_invisible(const Preset::Type& preset_type, const std::string& alias) const; // Save current preset of a provided type under a new name. If the name is different from the old one, // Unselected option would be reverted to the beginning values diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 14f535e2c9..44a72c8d63 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -300,6 +300,7 @@ struct Plater::priv static const std::regex pattern_any_amf; static const std::regex pattern_prusa; static const std::regex pattern_zip; + static const std::regex pattern_printRequest; priv(Plater *q, MainFrame *main_frame); ~priv(); @@ -597,6 +598,7 @@ const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::ica const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase); const std::regex Plater::priv::pattern_prusa(".*prusa", std::regex::icase); const std::regex Plater::priv::pattern_zip(".*zip", std::regex::icase); +const std::regex Plater::priv::pattern_printRequest(".*printRequest", std::regex::icase); Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q) @@ -1198,6 +1200,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf); const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf); const bool type_prusa = std::regex_match(path.string(), pattern_prusa); + const bool type_printRequest = std::regex_match(path.string(), pattern_printRequest); Slic3r::Model model; bool is_project_file = type_prusa; @@ -1370,7 +1373,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ convert_model_if(model, answer_convert_from_imperial_units == wxID_YES); } - if (model.looks_like_multipart_object()) { + if (!type_printRequest && model.looks_like_multipart_object()) { if (answer_consider_as_multi_part_objects == wxOK_DEFAULT) { RichMessageDialog dlg(q, _L( "This file contains several objects positioned at multiple heights.\n" @@ -1409,6 +1412,58 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (!model_object->instances.empty()) model_object->ensure_on_bed(is_project_file); + if (type_printRequest) { + for (ModelInstance* obj_instance : model_object->instances) { + obj_instance->set_offset(obj_instance->get_offset() + Slic3r::to_3d(this->bed.build_volume().bed_center(), -model_object->origin_translation(2))); + } + } + } + if (type_printRequest) { + assert(model.materials.size()); + + for (const auto& material : model.materials) { + std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias_invisible(Preset::Type::TYPE_SLA_MATERIAL, + Preset::remove_suffix_modified(material.first)); + Preset* prst = wxGetApp().preset_bundle->sla_materials.find_preset(preset_name, false); + if (!prst) { //did not find compatible profile + // try find alias of material comaptible with another print profile - if exists, use the print profile + auto& prints = wxGetApp().preset_bundle->sla_prints; + std::string edited_print_name = prints.get_edited_preset().name; + bool found = false; + for (auto it = prints.begin(); it != prints.end(); ++it) + { + if (it->name != edited_print_name) { + BOOST_LOG_TRIVIAL(error) << it->name; + wxGetApp().get_tab(Preset::Type::TYPE_SLA_PRINT)->select_preset(it->name, false); + preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias_invisible(Preset::Type::TYPE_SLA_MATERIAL, + Preset::remove_suffix_modified(material.first)); + prst = wxGetApp().preset_bundle->sla_materials.find_preset(preset_name, false); + if (prst) { + found = true; + break; + } + } + } + if (!found) { + // return to original print profile + wxGetApp().get_tab(Preset::Type::TYPE_SLA_PRINT)->select_preset(edited_print_name, false); + std::string notif_text = into_u8(_L("Material preset was not loaded:")); + notif_text += "\n - " + preset_name; + q->get_notification_manager()->push_notification(NotificationType::CustomNotification, + NotificationManager::NotificationLevel::PrintInfoNotificationLevel, notif_text); + break; + } + } + + PresetBundle* preset_bundle = wxGetApp().preset_bundle; + if (preset_bundle->sla_materials.get_selected_preset_name() != preset_name) { + preset_bundle->sla_materials.select_preset_by_name(preset_name, false, true); + preset_bundle->tmp_installed_presets = { preset_name }; + q->notify_about_installed_presets(); + wxGetApp().load_current_presets(false);// For this case we shouldn't check printer_presets + } + break; + } } if (one_by_one) { @@ -4814,7 +4869,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect) bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*=false*/) { - const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip)", std::regex::icase); + const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip|printRequest)", std::regex::icase); const std::regex pattern_gcode_drop(".*[.](gcode|g|bgcode|bgc)", std::regex::icase); std::vector paths;