From c9724ae6fdbdff248f2d7ed1d1a291490eb60acd Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Wed, 18 Jan 2023 11:00:20 +0100
Subject: [PATCH] Modify gcode post-process to allow to insert new gcode lines
 by backtracing from the current processed line.

Current state is a prototype and the new code is embedded into a new tech: ENABLE_GCODE_POSTPROCESS_BACKTRACE
---
 src/libslic3r/GCode/GCodeProcessor.cpp | 367 ++++++++++++++++++++++++-
 src/libslic3r/GCode/GCodeProcessor.hpp |   3 +
 src/libslic3r/GCodeReader.hpp          |  12 +
 src/libslic3r/Technologies.hpp         |   2 +
 4 files changed, 382 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 9b371e405..b5fc12e5a 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -441,6 +441,9 @@ void GCodeProcessorResult::reset() {
     max_print_height = 0.0f;
     settings_ids.reset();
     extruders_count = 0;
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    backtrace_enabled = false;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
     extruder_colors = std::vector<std::string>();
     filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
     filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
@@ -458,6 +461,9 @@ void GCodeProcessorResult::reset() {
     max_print_height = 0.0f;
     settings_ids.reset();
     extruders_count = 0;
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    backtrace_enabled = false;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
     extruder_colors = std::vector<std::string>();
     filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
     filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
@@ -551,6 +557,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
     m_producer = EProducer::PrusaSlicer;
     m_flavor = config.gcode_flavor;
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    m_result.backtrace_enabled = config.printer_model.value == "MK3MMU2" || config.printer_model.value == "MK3SMMU2S" ||
+        config.printer_model.value == "MK2.5MMU2" || config.printer_model.value == "MK2.5SMMU2S" || config.printer_model.value == "MK2SMM";
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
+
     size_t extruders_count = config.nozzle_diameter.values.size();
     m_result.extruders_count = extruders_count;
 
@@ -3461,18 +3472,259 @@ void GCodeProcessor::post_process()
         last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time);
     }
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    // Helper class to modify and export gcode to file
+    class ExportLines
+    {
+    public:
+        struct Backtrace
+        {
+            float time{ 60.0f };
+            unsigned int steps{ 10 };
+            float time_step() const { return time / float(steps); }
+        };
+
+        enum class EWriteType
+        {
+            BySize,
+            ByTime
+        };
+
+    private:
+        struct LineData
+        {
+            std::string line;
+            float time;
+        };
+
+#ifndef NDEBUG
+        class Statistics
+        {
+            ExportLines& m_parent;
+            size_t m_max_size{ 0 };
+            size_t m_lines_count{ 0 };
+            size_t m_max_lines_count{ 0 };
+
+        public:
+            explicit Statistics(ExportLines& parent)
+            : m_parent(parent)
+            {}
+
+            void add_line(size_t line_size) {
+                ++m_lines_count;
+                m_max_size = std::max(m_max_size, m_parent.get_size() + line_size);
+                m_max_lines_count = std::max(m_max_lines_count, m_lines_count);
+            }
+
+            void remove_line() { --m_lines_count; }
+            void remove_all_lines() { m_lines_count = 0; }
+        };
+
+        Statistics m_statistics;
+#endif // NDEBUG
+
+        EWriteType m_write_type{ EWriteType::BySize };
+        // Time machine containing g1 times cache
+        TimeMachine& m_machine;
+        // Current time
+        float m_time{ 0.0f };
+        // Current size in bytes
+        size_t m_size{ 0 };
+
+        // gcode lines cache
+        std::deque<LineData> m_lines;
+        size_t m_added_lines_counter{ 0 };
+        // map of gcode line ids from original to final 
+        // used to update m_result.moves[].gcode_id
+        std::vector<std::pair<size_t, size_t>> m_gcode_lines_map;
+
+        size_t m_curr_g1_id{ 0 };
+        size_t m_out_file_pos{ 0 };
+
+    public:
+        ExportLines(EWriteType type, TimeMachine& machine)
+#ifndef NDEBUG
+        : m_statistics(*this), m_write_type(type), m_machine(machine) {}
+#else
+        : m_write_type(type), m_machine(machine) {}
+#endif // NDEBUG
+
+        void update(size_t lines_counter, size_t g1_lines_counter) {
+            m_gcode_lines_map.push_back({ lines_counter, 0 });
+
+            if (g1_lines_counter == 0)
+                return;
+
+            auto init_it = m_machine.g1_times_cache.begin() + m_curr_g1_id;
+            auto it = init_it;
+            while (it != m_machine.g1_times_cache.end() && it->id < g1_lines_counter + 1) {
+                ++it;
+                ++m_curr_g1_id;
+            }
+
+            if (it != init_it || m_curr_g1_id == 0)
+                m_time = it->elapsed_time;
+        }
+
+        // add the given gcode line to the cache
+        void append_line(const std::string& line) {
+            m_lines.push_back({ line, m_time });
+#ifndef NDEBUG
+            m_statistics.add_line(line.length());
+#endif // NDEBUG
+            m_size += line.length();
+            ++m_added_lines_counter;
+            assert(!m_gcode_lines_map.empty());
+            m_gcode_lines_map.back().second = m_added_lines_counter;
+        }
+
+        // Insert the gcode lines required by the command cmd by backtracing into the cache
+        void insert_lines(const Backtrace& backtrace, const std::string& cmd, std::function<std::string(unsigned int, float, float)> line_inserter) {
+            assert(!m_lines.empty());
+            const float time_step = backtrace.time_step();
+            size_t rev_it_dist = 0; // distance from the end of the cache of the starting point of the backtrace
+            float last_time_insertion = 0.0f; // used to avoid inserting two lines at the same time
+            for (unsigned int i = 0; i < backtrace.steps; ++i) {
+                const float backtrace_time_i = (i + 1) * time_step;
+                const float time_threshold_i = m_time - backtrace_time_i;
+                auto rev_it = m_lines.rbegin() + rev_it_dist;
+                auto start_rev_it = rev_it;
+
+                // backtrace into the cache to find the place where to insert the line
+                while (rev_it != m_lines.rend() && rev_it->time > time_threshold_i && GCodeReader::GCodeLine::extract_cmd(rev_it->line) != cmd) {
+                    ++rev_it;
+                }
+
+                // we met the previous evenience of cmd. stop inserting lines
+                if (rev_it != m_lines.rend() && GCodeReader::GCodeLine::extract_cmd(rev_it->line) == cmd)
+                    break;
+
+                // insert the line for the current step
+                if (rev_it != m_lines.rend() && rev_it != start_rev_it && rev_it->time != last_time_insertion) {
+                    last_time_insertion = rev_it->time;
+                    const std::string out_line = line_inserter(i + 1, last_time_insertion, m_time - last_time_insertion);
+                    rev_it_dist = std::distance(m_lines.rbegin(), rev_it) + 1;
+                    const auto new_it = m_lines.insert(rev_it.base(), { out_line, rev_it->time });
+#ifndef NDEBUG
+                    m_statistics.add_line(out_line.length());
+#endif // NDEBUG
+                    m_size += out_line.length();
+                    // synchronize gcode lines map
+                    for (auto map_it = m_gcode_lines_map.rbegin(); map_it != m_gcode_lines_map.rbegin() + rev_it_dist - 1; ++map_it) {
+                        ++map_it->second;
+                    }
+
+                    ++m_added_lines_counter;
+                }
+            }
+        }
+
+        // write to file:
+        // m_write_type == EWriteType::ByTime - all lines older than m_time - backtrace_time
+        // m_write_type == EWriteType::BySize - all lines if current size is greater than 65535 bytes
+        void write(FilePtr& out, float backtrace_time, GCodeProcessorResult& result, const std::string& out_path) {
+            if (m_lines.empty())
+                return;
+
+            // collect lines to write into a single string
+            std::string out_string;
+            if (!m_lines.empty()) {
+                if (m_write_type == EWriteType::ByTime) {
+                    while (m_lines.front().time < m_time - backtrace_time) {
+                        const LineData& data = m_lines.front();
+                        out_string += data.line;
+                        m_size -= data.line.length();
+                        m_lines.pop_front();
+#ifndef NDEBUG
+                        m_statistics.remove_line();
+#endif // NDEBUG
+                    }
+                }
+                else {
+                    if (m_size > 65535) {
+                        while (!m_lines.empty()) {
+                            out_string += m_lines.front().line;
+                            m_lines.pop_front();
+                        }
+                        m_size = 0;
+#ifndef NDEBUG
+                        m_statistics.remove_all_lines();
+#endif // NDEBUG
+                    }
+                }
+            }
+
+            write_to_file(out, out_string, result, out_path);
+        }
+
+        // flush the current content of the cache to file
+        void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) {
+            // collect lines to flush into a single string
+            std::string out_string;
+            while (!m_lines.empty()) {
+                out_string += m_lines.front().line;
+                m_lines.pop_front();
+            }
+            m_size = 0;
+#ifndef NDEBUG
+            m_statistics.remove_all_lines();
+#endif // NDEBUG
+
+            write_to_file(out, out_string, result, out_path);
+        }
+
+        void synchronize_moves(GCodeProcessorResult& result) const {
+            auto it = m_gcode_lines_map.begin();
+            for (GCodeProcessorResult::MoveVertex& move : result.moves) {
+                while (it != m_gcode_lines_map.end() && it->first < move.gcode_id) {
+                    ++it;
+                }
+                if (it != m_gcode_lines_map.end() && it->first == move.gcode_id)
+                    move.gcode_id = it->second;
+            }
+        }
+
+        size_t get_size() const { return m_size; }
+
+    private:
+        void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path) {
+            if (!out_string.empty()) {
+                fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f);
+                if (ferror(out.f)) {
+                    out.close();
+                    boost::nowide::remove(out_path.c_str());
+                    throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n"));
+                }
+                for (size_t i = 0; i < out_string.size(); ++i) {
+                    if (out_string[i] == '\n')
+                        result.lines_ends.emplace_back(m_out_file_pos + i + 1);
+                }
+                m_out_file_pos += out_string.size();
+            }
+        }
+    };
+
+    ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]);
+#else
     // buffer line to export only when greater than 64K to reduce writing calls
     std::string export_line;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
     // replace placeholder lines with the proper final value
     // gcode_line is in/out parameter, to reduce expensive memory allocation
     auto process_placeholders = [&](std::string& gcode_line) {
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+        bool processed = false;
+#else
         unsigned int extra_lines_count = 0;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
         // remove trailing '\n'
         auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
 
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
         std::string ret;
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
         if (line.length() > 1) {
             line = line.substr(1);
             if (m_time_processor.export_remaining_time_enabled &&
@@ -3481,17 +3733,29 @@ void GCodeProcessor::post_process()
                     const TimeMachine& machine = m_time_processor.machines[i];
                     if (machine.enabled) {
                         // export pair <percent, remaining time>
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                        export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
+                            (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
+                            (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0));
+                        processed = true;
+#else
                         ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
                             (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
                             (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
                         ++extra_lines_count;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
                         // export remaining time to next printer stop
                         if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
-                            int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
+                            const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                            export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
+                            last_exported_stop[i] = to_export_stop;
+#else
                             ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
                             last_exported_stop[i] = to_export_stop;
                             ++extra_lines_count;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                         }
                     }
                 }
@@ -3505,7 +3769,12 @@ void GCodeProcessor::post_process()
                         sprintf(buf, "; estimated printing time (%s mode) = %s\n",
                             (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
                             get_time_dhms(machine.time).c_str());
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                        export_lines.append_line(buf);
+                        processed = true;
+#else
                         ret += buf;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                     }
                 }
                 for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
@@ -3516,16 +3785,25 @@ void GCodeProcessor::post_process()
                         sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
                             (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
                             get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str());
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                        export_lines.append_line(buf);
+                        processed = true;
+#else
                         ret += buf;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                     }
                 }
             }
         }
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+        return processed;
+#else
         if (!ret.empty())
             // Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
             gcode_line = ret;
         return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
     };
 
     std::vector<double> filament_mm(m_result.extruders_count, 0.0);
@@ -3599,10 +3877,16 @@ void GCodeProcessor::post_process()
         time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
         // Caches, to be modified
         &g1_times_cache_it, &last_exported_main, &last_exported_stop,
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+        &export_lines]
+#else
         // String output
         &export_line]
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
         (const size_t g1_lines_counter) {
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
         unsigned int exported_lines_count = 0;
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
         if (m_time_processor.export_remaining_time_enabled) {
             for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
                 const TimeMachine& machine = m_time_processor.machines[i];
@@ -3616,10 +3900,17 @@ void GCodeProcessor::post_process()
                         std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
                                                                 time_in_minutes(machine.time - it->elapsed_time) };
                         if (last_exported_main[i] != to_export_main) {
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                            export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
+                                to_export_main.first, to_export_main.second));
+#else
                             export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
                                 to_export_main.first, to_export_main.second);
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                             last_exported_main[i] = to_export_main;
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
                             ++exported_lines_count;
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
                         }
                         // export remaining time to next printer stop
                         auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
@@ -3629,9 +3920,15 @@ void GCodeProcessor::post_process()
                             if (last_exported_stop[i] != to_export_stop) {
                                 if (to_export_stop > 0) {
                                     if (last_exported_stop[i] != to_export_stop) {
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                                        export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
+#else
                                         export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                                         last_exported_stop[i] = to_export_stop;
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
                                         ++exported_lines_count;
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
                                     }
                                 }
                                 else {
@@ -3650,13 +3947,22 @@ void GCodeProcessor::post_process()
                                     }
 
                                     if (is_last) {
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                                        if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
+                                            export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
+                                        else
+                                            export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)));
+#else
                                         if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
                                             export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
                                         else
                                             export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
                                         last_exported_stop[i] = to_export_stop;
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
                                         ++exported_lines_count;
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
                                     }
                                 }
                             }
@@ -3665,12 +3971,26 @@ void GCodeProcessor::post_process()
                 }
             }
         }
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
         return exported_lines_count;
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
     };
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    // add lines XXX to exported gcode
+    auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) {
+        const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line);
+        export_lines.insert_lines(backtrace, cmd, [](unsigned int id, float time, float time_diff) {
+            const std::string out = "XYYY ; id:" + std::to_string(id) + " time:" + std::to_string(time) + " time diff:" + std::to_string(time_diff) + "\n";
+            return out;
+        });
+    };
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
+
+    m_result.lines_ends.clear();
+#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
     // helper function to write to disk
     size_t out_file_pos = 0;
-    m_result.lines_ends.clear();
     auto write_string = [this, &export_line, &out, &out_path, &out_file_pos](const std::string& str) {
         fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
         if (ferror(out.f)) {
@@ -3684,9 +4004,18 @@ void GCodeProcessor::post_process()
         out_file_pos += export_line.size();
         export_line.clear();
     };
+#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
     unsigned int line_id = 0;
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    // Backtrace data for Tx gcode lines
+    static const ExportLines::Backtrace backtrace_T = { 60.0f, 10 };
+    // In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed
+    // to flush the backtrace cache accordingly
+    float max_backtrace_time = 60.0f;
+#else
     std::vector<std::pair<unsigned int, unsigned int>> offsets;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
     {
         // Read the input stream 64kB at a time, extract lines and process them.
@@ -3710,14 +4039,39 @@ void GCodeProcessor::post_process()
                 gcode_line.insert(gcode_line.end(), it, it_end);
                 if (eol) {
                     ++line_id;
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                    export_lines.update(line_id, g1_lines_counter);
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
                     gcode_line += "\n";
                     // replace placeholder lines
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                    bool processed = process_placeholders(gcode_line);
+                    if (processed)
+                        gcode_line.clear();
+#else
                     auto [processed, lines_added_count] = process_placeholders(gcode_line);
                     if (processed && lines_added_count > 0)
                         offsets.push_back({ line_id, lines_added_count });
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                     if (!processed)
                         processed = process_used_filament(gcode_line);
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+                    if (!processed && !is_temporary_decoration(gcode_line)) {
+                        if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1"))
+                            // add lines M73 where needed
+                            process_line_G1(g1_lines_counter++);
+                        else if (m_result.backtrace_enabled && GCodeReader::GCodeLine::cmd_starts_with(gcode_line, "T")) {
+                            // add lines XXX where needed
+                            process_line_T(gcode_line, g1_lines_counter, backtrace_T);
+                            max_backtrace_time = std::max(max_backtrace_time, backtrace_T.time);
+                        }
+                    }
+
+                    if (!gcode_line.empty())
+                        export_lines.append_line(gcode_line);
+                    export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path);
+#else
                     if (!processed && !is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
                         // remove temporary lines, add lines M73 where needed
                         unsigned int extra_lines_count = process_line_G1(g1_lines_counter++);
@@ -3728,6 +4082,7 @@ void GCodeProcessor::post_process()
                     export_line += gcode_line;
                     if (export_line.length() > 65535)
                         write_string(export_line);
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
                     gcode_line.clear();
                 }
                 // Skip EOL.
@@ -3742,12 +4097,19 @@ void GCodeProcessor::post_process()
         }
     }
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    export_lines.flush(out, m_result, out_path);
+#else
     if (!export_line.empty())
         write_string(export_line);
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
     out.close();
     in.close();
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+    export_lines.synchronize_moves(m_result);
+#else
     // updates moves' gcode ids which have been modified by the insertion of the M73 lines
     unsigned int curr_offset_id = 0;
     unsigned int total_offset = 0;
@@ -3758,6 +4120,7 @@ void GCodeProcessor::post_process()
         }
         move.gcode_id += total_offset;
     }
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
 
     if (rename_file(out_path, m_result.filename))
         throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 05656c7fd..c63e095ee 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -125,6 +125,9 @@ namespace Slic3r {
         float max_print_height;
         SettingsIds settings_ids;
         size_t extruders_count;
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+        bool backtrace_enabled;
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
         std::vector<std::string> extruder_colors;
         std::vector<float> filament_diameters;
         std::vector<float> filament_densities;
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index 25ba6ee0b..d8bc99bf9 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -71,6 +71,18 @@ public:
             return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
         }
 
+#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
+        static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) {
+            return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0;
+        }
+
+        static std::string extract_cmd(const std::string& gcode_line) {
+            GCodeLine temp;
+            temp.m_raw = gcode_line;
+            return temp.cmd().data();
+        }
+#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
+
     private:
         std::string      m_raw;
         float            m_axis[NUM_AXES];
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 60d89c9e9..c33f24311 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -58,6 +58,8 @@
 
 // Enable alternative version of file_wildcards()
 #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1)
+// Enable gcode postprocess modified to allow for backward insertion of new lines
+#define ENABLE_GCODE_POSTPROCESS_BACKTRACE (1 && ENABLE_2_6_0_ALPHA1)
 
 
 #endif // _prusaslicer_technologies_h_