diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index cc53cd0e51..ebecaa61de 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2562,6 +2562,43 @@ void GCodeProcessorResult::reset() { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: this=%2% reset finished")%__LINE__%this; } +bool GCodeProcessorResult::rebuild_lines_ends() +{ + // lock_guard rather than the lock()/unlock() pair reset() uses: this reads a file and grows a + // vector, so a throw between the two would leave the mutex held forever. + std::lock_guard lock(result_mutex); + // A partially rebuilt map would have the G-code window slicing lines at wrong offsets all over + // again, so every failure below leaves lines_ends empty, which just hides the window. + lines_ends.clear(); + + FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (in.f == nullptr) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": cannot open " << filename << " to rebuild the G-code line offsets."; + return false; + } + + // Record the offset one past each '\n', matching what ExportLines emits during the export pass. + std::vector buffer(65536, 0); + size_t file_pos = 0; + for (;;) { + const size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f); + if (::ferror(in.f)) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": error while reading " << filename + << " to rebuild the G-code line offsets."; + lines_ends.clear(); + return false; + } + for (size_t i = 0; i < cnt_read; ++i) { + if (buffer[i] == '\n') + lines_ends.emplace_back(file_pos + i + 1); + } + file_pos += cnt_read; + if (cnt_read < buffer.size()) + break; + } + return true; +} + const std::vector> GCodeProcessor::Producers = { //BBS: OrcaSlicer is also "bambu". Otherwise the time estimation didn't work. //FIXME: Workaround and should be handled when do removing-bambu diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index f5bec9e826..9e8d3372d1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -317,6 +317,11 @@ class Print; BedType bed_type = BedType::btCount; void reset(); + // Re-scan this->filename and rebuild lines_ends from it. Needed whenever the exported G-code is + // modified in place after export (post-processing scripts), which shifts the byte offsets the + // G-code viewer uses to slice lines out of the memory-mapped file. Returns false and leaves + // lines_ends empty if the file cannot be read. + bool rebuild_lines_ends(); //BBS: add mutex for protection of gcode result mutable std::mutex result_mutex; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 64c52c6e72..25a4a46182 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -261,7 +261,13 @@ void BackgroundSlicingProcess::process_fff() // GCodeProcessorResult, so m_gcode_result->nozzle_group_result (consumed by the H2C print-dispatch // nozzle mapping) survives post-processing. No preservation guard is needed on this path. if (m_fff_print->is_BBL_printer()) { - run_post_process_scripts(m_temp_output_path, false, "File", m_temp_output_path, m_fff_print->full_print_config()); + if (run_post_process_scripts(m_temp_output_path, false, "File", m_temp_output_path, m_fff_print->full_print_config())) + // The scripts/plugins rewrote the very file the G-code viewer memory-maps, so every byte + // offset in m_gcode_result->lines_ends (built while exporting the pre-processed G-code) is + // now stale and GCodeWindow would slice the file mid-line. Re-scan it. Note this only + // realigns the line framing: if a script inserts or removes lines, the moves' gcode_id + // still refers to the pre-processed numbering and the highlighted line stays shifted. + m_gcode_result->rebuild_lines_ends(); } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": export gcode finished");