diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index deaa0b924..99d010d9c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -98,6 +98,8 @@ add_library(libslic3r STATIC GCode/ThumbnailData.hpp GCode/CoolingBuffer.cpp GCode/CoolingBuffer.hpp + GCode/FindReplace.cpp + GCode/FindReplace.hpp GCode/PostProcessor.cpp GCode/PostProcessor.hpp # GCode/PressureEqualizer.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a91f15b2a..14b1f8744 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1155,6 +1155,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_enable_extrusion_role_markers = false; #endif /* HAS_PRESSURE_EQUALIZER */ + if (! print.config().gcode_substitutions.values.empty()) + m_find_replace = make_unique(print.config()); + // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); @@ -1560,13 +1563,21 @@ void GCode::process_layers( [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string { return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); + const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&self = *this->m_find_replace.get()](std::string s) -> std::string { + return self.process_layer(std::move(s)); + }); const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [&output_stream](std::string s) { output_stream.write(s); } ); // The pipeline elements are joined using const references, thus no copying is performed. - if (m_spiral_vase) + if (m_spiral_vase && m_find_replace) + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); + else if (m_spiral_vase) tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); + else if (m_find_replace) + tbb::parallel_pipeline(12, generator & cooling & find_replace & output); else tbb::parallel_pipeline(12, generator & cooling & output); } @@ -1603,13 +1614,21 @@ void GCode::process_layers( [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string { return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); + const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&self = *this->m_find_replace.get()](std::string s) -> std::string { + return self.process_layer(std::move(s)); + }); const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [&output_stream](std::string s) { output_stream.write(s); } ); // The pipeline elements are joined using const references, thus no copying is performed. - if (m_spiral_vase) + if (m_spiral_vase && m_find_replace) + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); + else if (m_spiral_vase) tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); + else if (m_find_replace) + tbb::parallel_pipeline(12, generator & cooling & find_replace & output); else tbb::parallel_pipeline(12, generator & cooling & output); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1b2c560da..94bb47f85 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -10,6 +10,7 @@ #include "PrintConfig.hpp" #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" +#include "GCode/FindReplace.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" @@ -393,6 +394,7 @@ private: std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; + std::unique_ptr m_find_replace; #ifdef HAS_PRESSURE_EQUALIZER std::unique_ptr m_pressure_equalizer; #endif /* HAS_PRESSURE_EQUALIZER */ diff --git a/src/libslic3r/GCode/FindReplace.cpp b/src/libslic3r/GCode/FindReplace.cpp new file mode 100644 index 000000000..6d489b948 --- /dev/null +++ b/src/libslic3r/GCode/FindReplace.cpp @@ -0,0 +1,71 @@ +#include "FindReplace.hpp" +#include "../Utils.hpp" + +namespace Slic3r { + +GCodeFindReplace::GCodeFindReplace(const PrintConfig &print_config) +{ + const std::vector &subst = print_config.gcode_substitutions.values; + + if ((subst.size() % 3) != 0) + throw RuntimeError("Invalid length of gcode_substitutions parameter"); + + m_substitutions.reserve(subst.size() / 3); + for (size_t i = 0; i < subst.size(); i += 3) { + boost::regex pattern; + try { + pattern.assign(subst[i]); + } catch (const std::exception &ex) { + throw RuntimeError(std::string("Invalid gcode_substitutions parameter, failed to compile regular expression: ") + ex.what()); + } + m_substitutions.push_back({ std::move(pattern), subst[i + 1] }); + } +} + +class ToStringIterator +{ +public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + + ToStringIterator(std::string &data) : m_data(&data) {} + + ToStringIterator& operator=(const char val) { + size_t needs = m_data->size() + 1; + if (m_data->capacity() < needs) + m_data->reserve(next_highest_power_of_2(needs)); + m_data->push_back(val); + return *this; + } + + ToStringIterator& operator*() { return *this; } + ToStringIterator& operator++() { return *this; } + ToStringIterator operator++(int) { return *this; } + +private: + std::string *m_data; +}; + +std::string GCodeFindReplace::process_layer(const std::string &ain) +{ + std::string out; + const std::string *in = &ain; + std::string temp; + temp.reserve(in->size()); + + for (const Substitution &substitution : m_substitutions) { + temp.clear(); + temp.reserve(in->size()); + boost::regex_replace(ToStringIterator(temp), in->begin(), in->end(), + substitution.pattern, substitution.format, boost::match_default | boost::format_all); + std::swap(out, temp); + in = &out; + } + + return out; +} + +} diff --git a/src/libslic3r/GCode/FindReplace.hpp b/src/libslic3r/GCode/FindReplace.hpp new file mode 100644 index 000000000..57577e792 --- /dev/null +++ b/src/libslic3r/GCode/FindReplace.hpp @@ -0,0 +1,26 @@ +#ifndef slic3r_FindReplace_hpp_ +#define slic3r_FindReplace_hpp_ + +#include "../PrintConfig.hpp" + +#include + +namespace Slic3r { + +class GCodeFindReplace { +public: + GCodeFindReplace(const PrintConfig &print_config); + + std::string process_layer(const std::string &gcode); + +private: + struct Substitution { + boost::regex pattern; + std::string format; + }; + std::vector m_substitutions; +}; + +} + +#endif // slic3r_FindReplace_hpp_ diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 2048dc66e..b1a25829b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1357,6 +1357,12 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(0)); + def = this->add("gcode_substitutions", coStrings); + def->label = L("G-code substitutions"); + def->tooltip = L("Find / replace patterns in G-code lines and substitute them."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionStrings()); + def = this->add("high_current_on_filament_swap", coBool); def->label = L("High extruder current on filament swap"); def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6851ceb10..5f7c99911 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -641,6 +641,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, gcode_comments)) ((ConfigOptionEnum, gcode_flavor)) ((ConfigOptionBool, gcode_label_objects)) + ((ConfigOptionStrings, gcode_substitutions)) ((ConfigOptionString, layer_gcode)) ((ConfigOptionFloat, max_print_speed)) ((ConfigOptionFloat, max_volumetric_speed))