diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f3bffe331..27e5b478b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -747,8 +747,16 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); }); DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); +#if ENABLE_GCODE_WINDOW + if (result != nullptr) { + *result = std::move(m_processor.extract_result()); + // set the filename to the correct value + result->filename = path; + } +#else if (result != nullptr) *result = std::move(m_processor.extract_result()); +#endif // ENABLE_GCODE_WINDOW BOOST_LOG_TRIVIAL(debug) << "Finished processing gcode, " << log_memory_info(); if (rename_file(path_tmp, path)) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c4b2a3518..eb5c616a1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -6,6 +6,9 @@ #include #include #include +#if ENABLE_GCODE_WINDOW +#include +#endif // ENABLE_GCODE_WINDOW #include #include @@ -976,6 +979,9 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr } // process gcode +#if ENABLE_GCODE_WINDOW + m_result.filename = filename; +#endif // ENABLE_GCODE_WINDOW m_result.id = ++s_result_id; // 1st move must be a dummy move m_result.moves.emplace_back(MoveVertex()); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 05f9a2ce3..04d4c9eee 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -334,13 +334,15 @@ namespace Slic3r { std::vector filament; std::string printer; - void reset() - { + void reset() { print = ""; filament = std::vector(); printer = ""; } }; +#if ENABLE_GCODE_WINDOW + std::string filename; +#endif // ENABLE_GCODE_WINDOW unsigned int id; std::vector moves; Pointfs bed_shape; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 1c21e2f86..e5b6bd9d6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -99,6 +99,7 @@ #define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_3_1_ALPHA1) #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1) #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) +#define ENABLE_GCODE_WINDOW (1 && ENABLE_2_3_1_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 71441e037..2f2632e07 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -21,6 +21,9 @@ #include #include +#if ENABLE_GCODE_WINDOW +#include +#endif // ENABLE_GCODE_WINDOW #include #include #include @@ -278,7 +281,7 @@ void GCodeViewer::SequentialView::Marker::render() const imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":"); ImGui::SameLine(); char buf[1024]; - sprintf(buf, "X: %.2f, Y: %.2f, Z: %.2f", m_world_position(0), m_world_position(1), m_world_position(2)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position(0), m_world_position(1), m_world_position(2)); imgui.text(std::string(buf)); // force extra frame to automatically update window size @@ -295,6 +298,149 @@ void GCodeViewer::SequentialView::Marker::render() const ImGui::PopStyleVar(); } +#if ENABLE_GCODE_WINDOW +const unsigned int GCodeViewer::SequentialView::GCodeWindow::DefaultMaxLinesCount = 20; + +void GCodeViewer::SequentialView::GCodeWindow::load_gcode() +{ + m_gcode.clear(); + if (m_filename.empty()) + return; + + boost::nowide::ifstream f(m_filename); + std::string line; + while (std::getline(f, line)) { + m_gcode.push_back(line); + } + + m_file_size = static_cast(m_gcode.size()); + m_max_lines_count = std::min(DefaultMaxLinesCount, m_file_size); +} + +void GCodeViewer::SequentialView::GCodeWindow::start_mapping_file() +{ + std::cout << "GCodeViewer::SequentialView::GCodeWindow::start_mapping_file()\n"; +} + +void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() +{ + std::cout << "GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()\n"; +} + +void GCodeViewer::SequentialView::GCodeWindow::render(unsigned int curr_line_id) const +{ + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; + + if (!m_visible) + return; + + if (m_filename.empty() || m_gcode.empty()) + return; + + if (curr_line_id == 0) + return; + + // calculate visible range + unsigned int half_max_lines_count = m_max_lines_count / 2; + unsigned int start_id = (curr_line_id >= half_max_lines_count) ? curr_line_id - half_max_lines_count : 0; + unsigned int end_id = start_id + m_max_lines_count - 1; + if (end_id >= m_file_size) { + end_id = m_file_size - 1; + start_id = end_id - m_max_lines_count + 1; + } + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + imgui.set_next_window_pos(0.0f, 0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height(), ImGuiCond_Always, 0.0f, 0.5f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const auto& [id_offset, text_height] = ImGui::CalcTextSize(std::to_string(end_id).c_str()); + + for (unsigned int id = start_id; id <= end_id; ++id) { + const std::string& line = m_gcode[id]; + + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, line, 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]; + } + } + } + + // background rect for the current selected move + if (id == curr_line_id - 2) { + ImVec2 pos = ImGui::GetCursorScreenPos(); + const float window_border_size = ImGui::GetCurrentWindow()->WindowBorderSize; + draw_list->AddRectFilled({ window_border_size, pos.y - 1 }, { ImGui::GetCurrentWindow()->Size.x - window_border_size, pos.y + text_height + 1 }, + ImGui::GetColorU32({ 0.1f, 0.1f, 0.1f, 1.0f })); + } + + // render line number + ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR); + const std::string id_str = std::to_string(id + 1); + ImGui::SameLine(0.0f, id_offset - ImGui::CalcTextSize(id_str.c_str()).x); + imgui.text(id_str); + ImGui::PopStyleColor(); + + if (!command.empty() || !comment.empty()) + ImGui::SameLine(); + + // render command + if (!command.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, COMMAND_COLOR); + imgui.text(command); + ImGui::PopStyleColor(); + } + + // render command parameters + if (!parameters.empty()) { + ImGui::SameLine(0.0f, 0.0f); + imgui.text(parameters); + } + + // render comment + if (!comment.empty()) { + if (!command.empty()) + ImGui::SameLine(0.0f, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, COMMENT_COLOR); + imgui.text(comment); + ImGui::PopStyleColor(); + } + + if (id < end_id) + ImGui::NewLine(); + } + + imgui.end(); + ImGui::PopStyleVar(); +} + +void GCodeViewer::SequentialView::render() const +{ + marker.render(); + gcode_window.render(gcode_ids[current.last]); +} +#endif // ENABLE_GCODE_WINDOW + const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.75f, 0.75f, 0.75f }, // erNone { 1.00f, 0.90f, 0.30f }, // erPerimeter @@ -393,6 +539,11 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& // release gpu memory, if used reset(); +#if ENABLE_GCODE_WINDOW + m_sequential_view.gcode_window.set_filename(gcode_result.filename); + m_sequential_view.gcode_window.load_gcode(); +#endif // ENABLE_GCODE_WINDOW + load_toolpaths(gcode_result); if (m_layers.empty()) @@ -545,7 +696,9 @@ void GCodeViewer::reset() m_layers_z_range = { 0, 0 }; m_roles = std::vector(); m_time_statistics.reset(); - +#if ENABLE_GCODE_WINDOW + m_sequential_view.gcode_window.reset(); +#endif // ENABLE_GCODE_WINDOW #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -608,7 +761,11 @@ void GCodeViewer::render() const SequentialView* sequential_view = const_cast(&m_sequential_view); if (sequential_view->current.last != sequential_view->endpoints.last) { sequential_view->marker.set_world_position(sequential_view->current_position); +#if ENABLE_GCODE_WINDOW + sequential_view->render(); +#else sequential_view->marker.render(); +#endif // ENABLE_GCODE_WINDOW } render_shells(); render_legend(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 290c13d51..e8f9571fc 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -602,6 +602,29 @@ public: void render() const; }; +#if ENABLE_GCODE_WINDOW + class GCodeWindow + { + static const unsigned int DefaultMaxLinesCount; + bool m_visible{ false }; + unsigned int m_max_lines_count{ DefaultMaxLinesCount }; + unsigned int m_file_size{ 0 }; + std::string m_filename; + std::vector m_gcode; + + public: + void set_filename(const std::string& filename) { m_filename = filename; } + void load_gcode(); + void start_mapping_file(); + void stop_mapping_file(); + void reset() { m_filename.clear(); } + + void toggle_visibility() { m_visible = !m_visible; } + + void render(unsigned int curr_line_id) const; + }; +#endif // ENABLE_GCODE_WINDOW + struct Endpoints { size_t first{ 0 }; @@ -614,9 +637,16 @@ public: Endpoints last_current; Vec3f current_position{ Vec3f::Zero() }; Marker marker; +#if ENABLE_GCODE_WINDOW + GCodeWindow gcode_window; +#endif // ENABLE_GCODE_WINDOW #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER std::vector gcode_ids; #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER + +#if ENABLE_GCODE_WINDOW + void render() const; +#endif // ENABLE_GCODE_WINDOW }; enum class EViewType : unsigned char @@ -710,6 +740,12 @@ public: void export_toolpaths_to_obj(const char* filename) const; +#if ENABLE_GCODE_WINDOW + void start_mapping_gcode_file() { m_sequential_view.gcode_window.start_mapping_file(); } + void stop_mapping_gcode_file() { m_sequential_view.gcode_window.stop_mapping_file(); } + void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); } +#endif // ENABLE_GCODE_WINDOW + private: void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0aec25404..54c4844be 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1195,6 +1195,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) GLCanvas3D::~GLCanvas3D() { +#if ENABLE_GCODE_WINDOW + m_gcode_viewer.stop_mapping_gcode_file(); +#endif // ENABLE_GCODE_WINDOW reset_volumes(); } @@ -2621,6 +2624,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } case 'B': case 'b': { zoom_to_bed(); break; } +#if ENABLE_GCODE_WINDOW + case 'C': + case 'c': { m_gcode_viewer.toggle_gcode_window_visibility(); m_dirty = true; request_extra_frame(); break; } +#endif // ENABLE_GCODE_WINDOW case 'E': case 'e': { m_labels.show(!m_labels.is_shown()); m_dirty = true; break; } case 'G': @@ -3909,6 +3916,18 @@ void GLCanvas3D::mouse_up_cleanup() m_canvas->ReleaseMouse(); } +#if ENABLE_GCODE_WINDOW +void GLCanvas3D::start_mapping_gcode_file() +{ + m_gcode_viewer.start_mapping_gcode_file(); +} + +void GLCanvas3D::stop_mapping_gcode_file() +{ + m_gcode_viewer.stop_mapping_gcode_file(); +} +#endif // ENABLE_GCODE_WINDOW + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 10294931f..990c8c048 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -785,6 +785,11 @@ public: #endif } +#if ENABLE_GCODE_WINDOW + void start_mapping_gcode_file(); + void stop_mapping_gcode_file(); +#endif // ENABLE_GCODE_WINDOW + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 04e6769e4..c1efa324e 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -202,6 +202,9 @@ void KBShortcutsDialog::fill_shortcuts() { "D", L("Horizontal slider - Move active thumb Right") }, { "X", L("On/Off one layer mode of the vertical slider") }, { "L", L("Show/Hide Legend and Estimated printing time") }, +#if ENABLE_GCODE_WINDOW + { "C", L("Show/Hide G-code window") }, +#endif // ENABLE_GCODE_WINDOW }; m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6400bbbcc..b23d81174 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3289,8 +3289,15 @@ void Plater::priv::set_current_panel(wxPanel* panel) panel_sizer->Layout(); if (current_panel == view3D) { +#if ENABLE_GCODE_WINDOW + if (old_panel == preview) { + preview->get_canvas3d()->stop_mapping_gcode_file(); + preview->get_canvas3d()->unbind_event_handlers(); + } +#else if (old_panel == preview) preview->get_canvas3d()->unbind_event_handlers(); +#endif // ENABLE_GCODE_WINDOW view3D->get_canvas3d()->bind_event_handlers(); @@ -3315,6 +3322,9 @@ void Plater::priv::set_current_panel(wxPanel* panel) view3D->get_canvas3d()->unbind_event_handlers(); preview->get_canvas3d()->bind_event_handlers(); +#if ENABLE_GCODE_WINDOW + preview->get_canvas3d()->start_mapping_gcode_file(); +#endif // ENABLE_GCODE_WINDOW // see: Plater::priv::object_list_changed() // FIXME: it may be better to have a single function making this check and let it be called wherever needed