From cad97bc9a2843055bf31ba8cb78da4d940f7311d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 12:26:56 +0200 Subject: [PATCH] GCodeViewer - Update gcode window for binary gcode files --- src/libslic3r/GCode/GCodeProcessor.cpp | 52 ++++--- src/libslic3r/GCode/GCodeProcessor.hpp | 7 + src/libslic3r/GCodeReader.cpp | 9 ++ src/libslic3r/GCodeReader.hpp | 4 + src/slic3r/GUI/GCodeViewer.cpp | 199 +++++++++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 18 ++- 6 files changed, 221 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 253da95b13..ffb35c8e59 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -464,6 +464,9 @@ void GCodeProcessorResult::reset() { #else void GCodeProcessorResult::reset() { +#if ENABLE_BINARIZED_GCODE + is_binary_file = false; +#endif // ENABLE_BINARIZED_GCODE moves.clear(); lines_ends.clear(); bed_shape = Pointfs(); @@ -564,6 +567,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) #if ENABLE_BINARIZED_GCODE m_binarizer.set_enabled(config.gcode_binary); + m_result.is_binary_file = config.gcode_binary; #endif // ENABLE_BINARIZED_GCODE m_producer = EProducer::PrusaSlicer; @@ -1104,6 +1108,9 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi // process gcode m_result.filename = filename; +#if ENABLE_BINARIZED_GCODE + m_result.is_binary_file = false; +#endif // ENABLE_BINARIZED_GCODE m_result.id = ++s_result_id; initialize_result_moves(); size_t parse_line_callback_cntr = 10000; @@ -1122,6 +1129,16 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi } #if ENABLE_BINARIZED_GCODE +static void update_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +{ + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1); + } + if (out_file_pos != nullptr) + *out_file_pos += out_string.size(); +} + void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) { #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1272,6 +1289,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct apply_config(config); m_result.filename = filename; + m_result.is_binary_file = true; m_result.id = ++s_result_id; initialize_result_moves(); @@ -1287,7 +1305,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); - // TODO: Update m_result.lines_ends + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + update_out_file_pos(block.raw_data, lines_ends, nullptr); m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { this->process_gcode_line(line, true); @@ -3971,23 +3990,14 @@ void GCodeProcessor::post_process() if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } - else -#endif // ENABLE_BINARIZED_GCODE + else { write_to_file(out, out_string, result, out_path); -#if ENABLE_BINARIZED_GCODE - update_out_file_pos(out_string, result); -#endif // ENABLE_BINARIZED_GCODE - } - -#if ENABLE_BINARIZED_GCODE - void update_out_file_pos(const std::string& out_string, GCodeProcessorResult& result) { - 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); + update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } - m_out_file_pos += out_string.size(); - } +#else + write_to_file(out, out_string, result, out_path); #endif // ENABLE_BINARIZED_GCODE + } // flush the current content of the cache to file void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { @@ -4007,11 +4017,12 @@ void GCodeProcessor::post_process() if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } - else -#endif // ENABLE_BINARIZED_GCODE + else { write_to_file(out, out_string, result, out_path); -#if ENABLE_BINARIZED_GCODE - update_out_file_pos(out_string, result); + update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + } +#else + write_to_file(out, out_string, result, out_path); #endif // ENABLE_BINARIZED_GCODE } @@ -4312,6 +4323,9 @@ void GCodeProcessor::post_process() }; m_result.lines_ends.clear(); +#if ENABLE_BINARIZED_GCODE + m_result.lines_ends.emplace_back(std::vector()); +#endif // ENABLE_BINARIZED_GCODE unsigned int line_id = 0; // Backtrace data for Tx gcode lines diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index bef1cd68f3..1126432518 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -139,10 +139,17 @@ namespace Slic3r { }; std::string filename; +#if ENABLE_BINARIZED_GCODE + bool is_binary_file; +#endif // ENABLE_BINARIZED_GCODE unsigned int id; std::vector moves; // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. +#if ENABLE_BINARIZED_GCODE + std::vector> lines_ends; +#else std::vector lines_ends; +#endif // ENABLE_BINARIZED_GCODE Pointfs bed_shape; float max_print_height; SettingsIds settings_ids; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index cb0366757a..2b94963e93 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -195,11 +195,20 @@ bool GCodeReader::parse_file(const std::string &file, callback_t callback) return this->parse_file_internal(file, callback, [](size_t){}); } +#if ENABLE_BINARIZED_GCODE +bool GCodeReader::parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends) +{ + lines_ends.clear(); + lines_ends.push_back(std::vector()); + return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos) { lines_ends.front().emplace_back(file_pos); }); +} +#else bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector &lines_ends) { lines_ends.clear(); return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); }); } +#endif // ENABLE_BINARIZED_GCODE bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback) { diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 58f55fdcf7..7d1dad92a0 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -131,7 +131,11 @@ public: bool parse_file(const std::string &file, callback_t callback); // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code // as an overlay in the 3D scene. +#if ENABLE_BINARIZED_GCODE + bool parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends); +#else bool parse_file(const std::string &file, callback_t callback, std::vector &lines_ends); +#endif // ENABLE_BINARIZED_GCODE // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed. bool parse_file_raw(const std::string &file, raw_line_callback_t callback); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bfa6c87946..6425c359b3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -367,18 +367,22 @@ void GCodeViewer::SequentialView::Marker::render() ImGui::PopStyleVar(); } +#if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const GCodeProcessorResult& gcode_result) +{ + m_filename = gcode_result.filename; + m_is_binary_file = gcode_result.is_binary_file; + m_lines_ends = gcode_result.lines_ends; +} +#else void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) { -#if !ENABLE_BINARIZED_GCODE - assert(! m_file.is_open()); + assert(!m_file.is_open()); if (m_file.is_open()) return; -#endif // !ENABLE_BINARIZED_GCODE - m_filename = filename; + m_filename = filename; m_lines_ends = lines_ends; - -#if !ENABLE_BINARIZED_GCODE m_selected_line_id = 0; m_last_lines_size = 0; @@ -391,54 +395,150 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& fil BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; reset(); } -#endif // !ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE #if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::add_gcode_line_to_lines_cache(const std::string& src) +{ + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, src, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + + m_lines_cache.push_back({ command, parameters, comment }); +} + void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, size_t curr_line_id) { - auto update_lines = [this]() { + auto update_lines_ascii = [this]() { m_lines_cache.clear(); m_lines_cache.reserve(m_cache_range.size()); + const std::vector& lines_ends = m_lines_ends.front(); FILE* file = boost::nowide::fopen(m_filename.c_str(), "rb"); if (file != nullptr) { - for (size_t id = *m_cache_range.start_id; id <= *m_cache_range.end_id; ++id) { + for (size_t id = *m_cache_range.min; id <= *m_cache_range.max; ++id) { assert(id > 0); // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; + const size_t begin = id == 1 ? 0 : lines_ends[id - 2]; + const size_t len = lines_ends[id - 1] - begin; std::string gline(len, '\0'); - fseek(file, start, SEEK_SET); + fseek(file, begin, SEEK_SET); const size_t rsize = fread((void*)gline.data(), 1, len, file); if (ferror(file) || rsize != len) { m_lines_cache.clear(); break; } - std::string command; - std::string parameters; - std::string comment; + add_gcode_line_to_lines_cache(gline); + } + fclose(file); + } + }; - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); + auto update_lines_binary = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; + size_t cumulative_lines_count = 0; + std::vector cumulative_lines_counts; + cumulative_lines_counts.reserve(m_lines_ends.size()); + for (size_t i = 0; i < m_lines_ends.size(); ++i) { + cumulative_lines_count += m_lines_ends[i].size(); + cumulative_lines_counts.emplace_back(cumulative_lines_count); + } + + size_t first_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.min <= cumulative_lines_counts[i]) { + first_block_id = i; + break; + } + } + size_t last_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.max <= cumulative_lines_counts[i]) { + last_block_id = i; + break; + } + } + assert(last_block_id >= first_block_id); + + FilePtr file(boost::nowide::fopen(m_filename.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + if (res == EResult::Success) { + for (size_t i = 0; i < first_block_id; ++i) { + skip_block(*file.f, file_header, block_header); + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; + } + } + + for (size_t i = first_block_id; i <= last_block_id; ++i) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) { + m_lines_cache.clear(); + return; + } + + const size_t ref_id = (i == 0) ? 0 : i - 1; + const size_t first_line_id = (i == 0) ? *m_cache_range.min : + (*m_cache_range.min - 1 >= cumulative_lines_counts[ref_id]) ? *m_cache_range.min - cumulative_lines_counts[ref_id] : 1; + const size_t last_line_id = (*m_cache_range.max - 1 <= cumulative_lines_counts[i]) ? + (i == 0) ? *m_cache_range.max : *m_cache_range.max - cumulative_lines_counts[ref_id] : m_lines_ends[i].size() - 1; + + for (size_t j = first_line_id; j <= last_line_id; ++j) { + const size_t begin = (j == 1) ? 0 : m_lines_ends[i][j - 2]; + const size_t end = m_lines_ends[i][j - 1]; + std::string gline; + gline.insert(gline.end(), block.raw_data.begin() + begin, block.raw_data.begin() + end); + add_gcode_line_to_lines_cache(gline); + } + + if (ftell(file.f) == file_size) + break; + + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; } } } - m_lines_cache.push_back({ command, parameters, comment }); } - fclose(file); } }; @@ -463,13 +563,20 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s if (visible_lines_count == 0) return; + if (m_lines_ends.empty() || m_lines_ends.front().empty()) + return; + auto resize_range = [&](Range& range, size_t lines_count) { const size_t half_lines_count = lines_count / 2; - range.start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; - range.end_id = *range.start_id + lines_count - 1; - if (*range.end_id >= m_lines_ends.size()) { - range.end_id = m_lines_ends.size() - 1; - range.start_id = *range.end_id - lines_count + 1; + range.min = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; + range.max = *range.min + lines_count - 1; + size_t lines_ends_count = 0; + for (const auto& le : m_lines_ends) { + lines_ends_count += le.size(); + } + if (*range.max >= lines_ends_count) { + range.max = lines_ends_count - 1; + range.min = *range.max - lines_count + 1; } }; @@ -480,11 +587,17 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s // update cache if needed if (m_cache_range.empty() || !m_cache_range.contains(visible_range)) { resize_range(m_cache_range, 4 * visible_range.size()); - update_lines(); + if (m_is_binary_file) + update_lines_binary(); + else + update_lines_ascii(); } + if (m_lines_cache.empty()) + return; + // line number's column width - const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.end_id).c_str()).x; + const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.max).c_str()).x; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -525,8 +638,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s // render text lines size_t max_line_length = 0; - for (size_t id = *visible_range.start_id; id <= *visible_range.end_id; ++id) { - const Line& line = m_lines_cache[id - *m_cache_range.start_id]; + for (size_t id = *visible_range.min; id <= *visible_range.max; ++id) { + const Line& line = m_lines_cache[id - *m_cache_range.min]; // rect around the current selected line if (id == curr_line_id) { @@ -729,15 +842,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.end(); ImGui::PopStyleVar(); } -#endif // ENABLE_BINARIZED_GCODE -#if !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { if (m_file.is_open()) m_file.close(); } -#endif // !ENABLE_BINARIZED_GCODE +#endif // ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::render(float legend_height) { @@ -917,7 +1028,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr // release gpu memory, if used reset(); +#if ENABLE_BINARIZED_GCODE + m_sequential_view.gcode_window.load_gcode(gcode_result); +#else m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); +#endif // ENABLE_BINARIZED_GCODE if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 583619e623..60c350afe2 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -686,29 +686,30 @@ public: struct Range { - std::optional start_id; - std::optional end_id; + std::optional min; + std::optional max; bool empty() const { - return !start_id.has_value() || !end_id.has_value(); + return !min.has_value() || !max.has_value(); } bool contains(const Range& other) const { - return !this->empty() && !other.empty() && *this->start_id <= *other.start_id && *this->end_id >= other.end_id; + return !this->empty() && !other.empty() && *this->min <= *other.min && *this->max >= other.max; } size_t size() const { - return empty() ? 0 : *this->end_id - *this->start_id + 1; + return empty() ? 0 : *this->max - *this->min + 1; } }; bool m_visible{ true }; std::string m_filename; + bool m_is_binary_file{ false }; // map for accessing data in file by line number - std::vector m_lines_ends; + std::vector> m_lines_ends; std::vector m_lines_cache; Range m_cache_range; size_t m_max_line_length{ 0 }; public: - void load_gcode(const std::string& filename, const std::vector& lines_ends); + void load_gcode(const GCodeProcessorResult& gcode_result); void reset() { m_lines_ends.clear(); m_lines_cache.clear(); @@ -716,6 +717,9 @@ public: } void toggle_visibility() { m_visible = !m_visible; } void render(float top, float bottom, size_t curr_line_id); + + private: + void add_gcode_line_to_lines_cache(const std::string& src); }; #else class GCodeWindow