From 3b6d334d7bb0331e372cde14c69e153810fc0cbe Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 2 Mar 2020 15:13:23 +0100
Subject: [PATCH] ENABLE_GCODE_VIEWER - Basic framework for new gcode viewer

---
 src/PrusaSlicer.cpp                         |  4 +++
 src/libslic3r/GCode.cpp                     | 34 +++++++++++++++++----
 src/libslic3r/GCode.hpp                     | 10 +++---
 src/libslic3r/GCode/GCodeProcessor.cpp      | 30 ++++++++++--------
 src/libslic3r/GCode/GCodeProcessor.hpp      | 32 +++++++++++++------
 src/libslic3r/Print.cpp                     | 12 +++++---
 src/libslic3r/Print.hpp                     |  9 ++++--
 src/libslic3r/Technologies.hpp              |  2 --
 src/slic3r/GUI/BackgroundSlicingProcess.cpp | 22 +++++++++++--
 src/slic3r/GUI/BackgroundSlicingProcess.hpp |  6 ++++
 src/slic3r/GUI/GLCanvas3D.cpp               |  6 ++++
 src/slic3r/GUI/GLCanvas3D.hpp               |  7 +++++
 src/slic3r/GUI/GUI_Preview.cpp              | 12 ++++++++
 src/slic3r/GUI/GUI_Preview.hpp              | 15 +++++++--
 src/slic3r/GUI/Plater.cpp                   | 10 ++++++
 tests/fff_print/test_data.cpp               |  6 +++-
 tests/fff_print/test_model.cpp              |  4 +++
 17 files changed, 173 insertions(+), 48 deletions(-)

diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 048aea886..4557caa7c 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -443,7 +443,11 @@ int CLI::run(int argc, char **argv)
                         print->process();
                         if (printer_technology == ptFFF) {
                             // The outfile is processed by a PlaceholderParser.
+#if ENABLE_GCODE_VIEWER
+                            outfile = fff_print.export_gcode(outfile, nullptr, nullptr);
+#else
                             outfile = fff_print.export_gcode(outfile, nullptr);
+#endif // ENABLE_GCODE_VIEWER
                             outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
                         } else {
                             outfile = sla_print.output_filepath(outfile);
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 6717e961d..701ca4377 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -698,11 +698,13 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
     return layers_to_print;
 }
 
-#if ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
+#elif ENABLE_THUMBNAIL_GENERATOR
 void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
 #else
 void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data)
-#endif // ENABLE_THUMBNAIL_GENERATOR
+#endif // ENABLE_GCODE_VIEWER
 {
     PROFILE_CLEAR();
 
@@ -761,13 +763,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
         throw std::runtime_error(msg);
     }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_GCODE_VIEWER
-    m_processor.apply_config(print->config());
-    m_processor.reset();
     m_processor.process_file(path_tmp);
+    if (result != nullptr)
+        *result = std::move(m_processor.extract_result());
 #endif // ENABLE_GCODE_VIEWER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
     GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data();
     GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data();
@@ -905,6 +905,25 @@ namespace DoExport {
 	    analyzer.set_gcode_flavor(config.gcode_flavor);
 	}
 
+#if ENABLE_GCODE_VIEWER
+    static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor)
+    {
+        processor.reset();
+        processor.apply_config(config);
+
+        // send extruder offset data to processor
+        unsigned int num_extruders = static_cast<unsigned int>(config.nozzle_diameter.values.size());
+        GCodeProcessor::ExtruderOffsetsMap extruder_offsets;
+        for (unsigned int id = 0; id < num_extruders; ++id)
+        {
+            Vec2d offset = config.extruder_offset.get_at(id);
+            if (!offset.isApprox(Vec2d::Zero()))
+                extruder_offsets[id] = offset;
+        }
+        processor.set_extruder_offsets(extruder_offsets);
+    }
+#endif // ENABLE_GCODE_VIEWER
+
 	static double autospeed_volumetric_limit(const Print &print)
 	{
 	    // get the minimum cross-section used in the print
@@ -1142,6 +1161,9 @@ void GCode::_do_export(Print& print, FILE* file)
     	// modifies the following:
     	m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled);
     DoExport::init_gcode_analyzer(print.config(), m_analyzer);
+#if ENABLE_GCODE_VIEWER
+    DoExport::init_gcode_processor(print.config(), m_processor);
+#endif // ENABLE_GCODE_VIEWER
 
     // resets analyzer's tracking data
     m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index e3da956c2..4b66d1521 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -14,11 +14,9 @@
 #include "GCode/SpiralVase.hpp"
 #include "GCode/ToolOrdering.hpp"
 #include "GCode/WipeTower.hpp"
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_GCODE_VIEWER
 #include "GCode/GCodeProcessor.hpp"
 #endif // ENABLE_GCODE_VIEWER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #include "GCodeTimeEstimator.hpp"
 #include "EdgeGrid.hpp"
 #include "GCode/Analyzer.hpp"
@@ -171,11 +169,13 @@ public:
 
     // throws std::runtime_exception on error,
     // throws CanceledException through print->throw_if_canceled().
-#if ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+    void            do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
+#elif ENABLE_THUMBNAIL_GENERATOR
     void            do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
 #else
     void            do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
-#endif // ENABLE_THUMBNAIL_GENERATOR
+#endif // ENABLE_GCODE_VIEWER
 
     // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
     const Vec2d&    origin() const { return m_origin; }
@@ -388,12 +388,10 @@ private:
     // Analyzer
     GCodeAnalyzer m_analyzer;
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_GCODE_VIEWER
     // Processor
     GCodeProcessor m_processor;
 #endif // ENABLE_GCODE_VIEWER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
     // Write a string into a file.
     void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 08066ee57..493321a6e 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -8,30 +8,26 @@ static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
 
 namespace Slic3r {
 
-void GCodeProcessor::apply_config(const PrintConfig& config)
-{
-    m_parser.apply_config(config);
-}
-
 void GCodeProcessor::reset()
 {
     m_units = EUnits::Millimeters;
     m_global_positioning_type = EPositioningType::Absolute;
     m_e_local_positioning_type = EPositioningType::Absolute;
     
-    ::memset(m_start_position.data(), 0, sizeof(AxisCoords));
-    ::memset(m_end_position.data(), 0, sizeof(AxisCoords));
-    ::memset(m_origin.data(), 0, sizeof(AxisCoords));
+    std::fill(m_start_position.begin(), m_start_position.end(), 0.0f);
+    std::fill(m_end_position.begin(), m_end_position.end(), 0.0f);
+    std::fill(m_origin.begin(), m_origin.end(), 0.0f);
 
     m_feedrate = 0.0f;
+    m_extruder_id = 0;
 
-    m_moves.clear();
+    m_result.reset();
 }
 
 void GCodeProcessor::process_file(const std::string& filename)
 {
-    MoveStep start_step {};
-    m_moves.emplace_back(start_step);
+    MoveVertex start_vertex {};
+    m_result.moves.emplace_back(start_vertex);
     m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { process_gcode_line(line); });
     int a = 0;
 }
@@ -149,9 +145,17 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
     else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f))
         move_type = EMoveType::Travel;
 
+    // correct position by extruder offset
+    Vec3d extruder_offset = Vec3d::Zero();
+    auto it = m_extruder_offsets.find(m_extruder_id);
+    if (it != m_extruder_offsets.end())
+        extruder_offset = Vec3d(it->second(0), it->second(1), 0.0);
 
-    MoveStep move_step { m_end_position, m_feedrate, move_type };
-    m_moves.emplace_back(move_step);
+    MoveVertex vertex;
+    vertex.position = Vec3d(m_end_position[0], m_end_position[1], m_end_position[2]) + extruder_offset;
+    vertex.feedrate = m_feedrate;
+    vertex.type = move_type;
+    m_result.moves.emplace_back(vertex);
 
 /*
     std::cout << "start: ";
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 0a3dae032..7fa1733b7 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -33,15 +33,24 @@ namespace Slic3r {
             Num_Types
         };
 
-        struct MoveStep
+        struct MoveVertex
         {
-            AxisCoords position; // mm
-            float feedrate; // mm/s
-            EMoveType type;
+            Vec3d position{ Vec3d::Zero() }; // mm
+            float feedrate{ 0.0f }; // mm/s
+            // type of the move terminating at this vertex
+            EMoveType type{ EMoveType::Noop };
         };
 
-        using MoveStepsList = std::vector<MoveStep>;
+    public:
+        typedef std::map<unsigned int, Vec2d> ExtruderOffsetsMap;
 
+        struct Result
+        {
+            std::vector<MoveVertex> moves;
+            void reset() { moves = std::vector<MoveVertex>(); }
+        };
+
+    private:
         GCodeReader m_parser;
 
         EUnits m_units;
@@ -53,17 +62,22 @@ namespace Slic3r {
         AxisCoords m_origin;         // mm
 
         float m_feedrate; // mm/s
+        unsigned int m_extruder_id;
+        ExtruderOffsetsMap m_extruder_offsets;
 
-        MoveStepsList m_moves;
-
+        Result m_result;
 
     public:
         GCodeProcessor() { reset(); }
 
-        void apply_config(const PrintConfig& config);
-
+        void apply_config(const PrintConfig& config) { m_parser.apply_config(config); }
         void reset();
 
+        void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets) { m_extruder_offsets = extruder_offsets; }
+
+        const Result& get_result() const { return m_result; }
+        Result&& extract_result() { return std::move(m_result); }
+
         // Process the gcode contained in the file with the given filename
         // Return false if any error occourred
         void process_file(const std::string& filename);
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index d5a1fa178..d342c9056 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1619,11 +1619,13 @@ void Print::process()
 // The export_gcode may die for various reasons (fails to process output_filename_format,
 // write error into the G-code, cannot execute post-processing scripts).
 // It is up to the caller to show an error message.
-#if ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
+#elif ENABLE_THUMBNAIL_GENERATOR
 std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
 #else
 std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
-#endif // ENABLE_THUMBNAIL_GENERATOR
+#endif // ENABLE_GCODE_VIEWER
 {
     // output everything to a G-code file
     // The following call may die if the output_filename_format template substitution fails.
@@ -1640,11 +1642,13 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa
 
     // The following line may die for multiple reasons.
     GCode gcode;
-#if ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+    gcode.do_export(this, path.c_str(), preview_data, result, thumbnail_cb);
+#elif ENABLE_THUMBNAIL_GENERATOR
     gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb);
 #else
     gcode.do_export(this, path.c_str(), preview_data);
-#endif // ENABLE_THUMBNAIL_GENERATOR
+#endif // ENABLE_GCODE_VIEWER
     return path.c_str();
 }
 
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 7b326472e..ef5548222 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -14,6 +14,9 @@
 #if ENABLE_THUMBNAIL_GENERATOR
 #include "GCode/ThumbnailData.hpp"
 #endif // ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+#include "GCode/GCodeProcessor.hpp"
+#endif // ENABLE_GCODE_VIEWER
 
 #include "libslic3r.h"
 
@@ -364,11 +367,13 @@ public:
     void                process() override;
     // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
     // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
-#if ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+    std::string         export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
+#elif ENABLE_THUMBNAIL_GENERATOR
     std::string         export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
 #else
     std::string         export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
-#endif // ENABLE_THUMBNAIL_GENERATOR
+#endif // ENABLE_GCODE_VIEWER
 
     // methods for handling state
     bool                is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index e5a2f3fae..4cebe98b1 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -48,7 +48,6 @@
 #define ENABLE_2_2_0_BETA1 1
 
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 //==================
 // 2.3.0.alpha1 techs
 //==================
@@ -56,7 +55,6 @@
 
 // Enable G-Code viewer
 #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1)
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 
 #endif // _technologies_h_
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 548a19f77..126a77ae7 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -89,11 +89,13 @@ void BackgroundSlicingProcess::process_fff()
 	assert(m_print == m_fff_print);
     m_print->process();
 	wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
-#if ENABLE_THUMBNAIL_GENERATOR
-    m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
+#if ENABLE_GCODE_VIEWER
+	m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_gcode_result, m_thumbnail_cb);
+#elif ENABLE_THUMBNAIL_GENERATOR
+	m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
 #else
     m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
-#endif // ENABLE_THUMBNAIL_GENERATOR
+#endif // ENABLE_GCODE_VIEWER
 
 	if (this->set_step_started(bspsGCodeFinalize)) {
 	    if (! m_export_path.empty()) {
@@ -377,6 +379,19 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
 	assert(m_print != nullptr);
 	assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology());
 	Print::ApplyStatus invalidated = m_print->apply(model, config);
+#if ENABLE_GCODE_VIEWER
+	if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
+		!this->m_fff_print->is_step_done(psGCodeExport))
+	{
+		// Some FFF status was invalidated, and the G-code was not exported yet.
+		// Let the G-code preview UI know that the final G-code preview is not valid.
+		// In addition, this early memory deallocation reduces memory footprint.
+		if (m_gcode_preview_data != nullptr)
+			m_gcode_preview_data->reset();
+		else if (m_gcode_result != nullptr)
+			m_gcode_result->reset();
+	}
+#else
 	if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
 		m_gcode_preview_data != nullptr && ! this->m_fff_print->is_step_done(psGCodeExport)) {
 		// Some FFF status was invalidated, and the G-code was not exported yet.
@@ -384,6 +399,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
 		// In addition, this early memory deallocation reduces memory footprint.
 		m_gcode_preview_data->reset();
 	}
+#endif // ENABLE_GCODE_VIEWER
 	return invalidated;
 }
 
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
index c8ece38f0..5b6f09d27 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -52,6 +52,9 @@ public:
 #if ENABLE_THUMBNAIL_GENERATOR
     void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
 #endif // ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+	void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; }
+#endif // ENABLE_GCODE_VIEWER
 
 	// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
 	// and the background processing will transition into G-code export.
@@ -159,6 +162,9 @@ private:
     // Callback function, used to write thumbnails into gcode.
     ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
 #endif // ENABLE_THUMBNAIL_GENERATOR
+#if ENABLE_GCODE_VIEWER
+	GCodeProcessor::Result* m_gcode_result = nullptr;
+#endif // ENABLE_GCODE_VIEWER
 	// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
 	std::string 				m_temp_output_path;
 	// Output path provided by the user. The output path may be set even if the slicing is running,
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index b5319a2f1..b96b8b47f 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -2480,6 +2480,12 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio
 	volume->indexed_vertex_array.finalize_geometry(gl_initialized);
 }
 
+#if ENABLE_GCODE_VIEWER
+void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result)
+{
+}
+#endif // ENABLE_GCODE_VIEWER
+
 void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
 {
     const Print *print = this->fff_print();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 9ae127880..0dc2b26dd 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -13,6 +13,9 @@
 #include "Gizmos/GLGizmosManager.hpp"
 #include "GUI_ObjectLayers.hpp"
 #include "MeshUtils.hpp"
+#if ENABLE_GCODE_VIEWER
+#include "libslic3r/GCode/GCodeProcessor.hpp"
+#endif // ENABLE_GCODE_VIEWER
 
 #include <float.h>
 
@@ -577,6 +580,10 @@ public:
 
     void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
 
+#if ENABLE_GCODE_VIEWER
+    void load_gcode_preview_2(const GCodeProcessor::Result& gcode_result);
+#endif // ENABLE_GCODE_VIEWER
+
     void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
     void load_sla_preview();
     void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 5afdb3bb4..d0493adfc 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -163,9 +163,15 @@ void View3D::render()
         m_canvas->set_as_dirty();
 }
 
+#if ENABLE_GCODE_VIEWER
 Preview::Preview(
     wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, 
+    BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process_func)
+#else
+Preview::Preview(
+    wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
     BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process_func)
+#endif // ENABLE_GCODE_VIEWER
     : m_canvas_widget(nullptr)
     , m_canvas(nullptr)
     , m_double_slider_sizer(nullptr)
@@ -181,6 +187,9 @@ Preview::Preview(
     , m_config(config)
     , m_process(process)
     , m_gcode_preview_data(gcode_preview_data)
+#if ENABLE_GCODE_VIEWER
+    , m_gcode_result(gcode_result)
+#endif // ENABLE_GCODE_VIEWER
     , m_number_extruders(1)
     , m_preferred_color_mode("feature")
     , m_loaded(false)
@@ -860,6 +869,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
         if (gcode_preview_data_valid) {
             // Load the real G-code preview.
             m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
+#if ENABLE_GCODE_VIEWER
+            m_canvas->load_gcode_preview_2(*m_gcode_result);
+#endif // ENABLE_GCODE_VIEWER
             m_loaded = true;
         } else {
             // Load the initial preview based on slices, not the final G-code.
diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp
index cc8f15325..f0e7abfec 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -6,6 +6,9 @@
 
 #include <string>
 #include "libslic3r/Model.hpp"
+#if ENABLE_GCODE_VIEWER
+#include "libslic3r/GCode/GCodeProcessor.hpp"
+#endif // ENABLE_GCODE_VIEWER
 
 class wxNotebook;
 class wxGLCanvas;
@@ -90,6 +93,9 @@ class Preview : public wxPanel
     DynamicPrintConfig* m_config;
     BackgroundSlicingProcess* m_process;
     GCodePreviewData* m_gcode_preview_data;
+#if ENABLE_GCODE_VIEWER
+    GCodeProcessor::Result* m_gcode_result;
+#endif // ENABLE_GCODE_VIEWER
 
 #ifdef __linux__
     // We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955.
@@ -109,8 +115,13 @@ class Preview : public wxPanel
     DoubleSlider::Control*       m_slider {nullptr};
 
 public:
-    Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, 
-        BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = [](){});
+#if ENABLE_GCODE_VIEWER
+    Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
+        BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process = []() {});
+#else
+    Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
+        BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = []() {});
+#endif // ENABLE_GCODE_VIEWER
     virtual ~Preview();
 
     wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 317181887..3886d5f35 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1440,6 +1440,9 @@ struct Plater::priv
     Slic3r::Model               model;
     PrinterTechnology           printer_technology = ptFFF;
     Slic3r::GCodePreviewData    gcode_preview_data;
+#if ENABLE_GCODE_VIEWER
+    Slic3r::GCodeProcessor::Result gcode_result;
+#endif // ENABLE_GCODE_VIEWER
 
     // GUI elements
     wxSizer* panel_sizer{ nullptr };
@@ -1990,6 +1993,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
     background_process.set_fff_print(&fff_print);
     background_process.set_sla_print(&sla_print);
     background_process.set_gcode_preview_data(&gcode_preview_data);
+#if ENABLE_GCODE_VIEWER
+    background_process.set_gcode_result(&gcode_result);
+#endif // ENABLE_GCODE_VIEWER
 #if ENABLE_THUMBNAIL_GENERATOR
     background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
         {
@@ -2015,7 +2021,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
     this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this);
 
     view3D = new View3D(q, bed, camera, view_toolbar, &model, config, &background_process);
+#if ENABLE_GCODE_VIEWER
+    preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, &gcode_result, [this]() { schedule_background_process(); });
+#else
     preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); });
+#endif // ENABLE_GCODE_VIEWER
 
     panels.push_back(view3D);
     panels.push_back(preview);
diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp
index b3c16f4b0..50b338325 100644
--- a/tests/fff_print/test_data.cpp
+++ b/tests/fff_print/test_data.cpp
@@ -244,8 +244,12 @@ std::string gcode(Print & print)
 	boost::filesystem::path temp = boost::filesystem::unique_path();
     print.set_status_silent();
     print.process();
+#if ENABLE_GCODE_VIEWER
+    print.export_gcode(temp.string(), nullptr, nullptr);
+#else
     print.export_gcode(temp.string(), nullptr);
-	std::ifstream t(temp.string());
+#endif // ENABLE_GCODE_VIEWER
+    std::ifstream t(temp.string());
 	std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
 	boost::nowide::remove(temp.string().c_str());
 	return str;
diff --git a/tests/fff_print/test_model.cpp b/tests/fff_print/test_model.cpp
index 3378a8363..3d3b54ef0 100644
--- a/tests/fff_print/test_model.cpp
+++ b/tests/fff_print/test_model.cpp
@@ -50,7 +50,11 @@ SCENARIO("Model construction", "[Model]") {
 				print.apply(model, config);
 				print.process();
 				boost::filesystem::path temp = boost::filesystem::unique_path();
+#if ENABLE_GCODE_VIEWER
+				print.export_gcode(temp.string(), nullptr, nullptr);
+#else
 				print.export_gcode(temp.string(), nullptr);
+#endif // ENABLE_GCODE_VIEWER
 				REQUIRE(boost::filesystem::exists(temp));
 				REQUIRE(boost::filesystem::is_regular_file(temp));
 				REQUIRE(boost::filesystem::file_size(temp) > 0);