diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/xs/src/libslic3r/GCode/PreviewData.cpp
new file mode 100644
index 000000000..fc5e4f02b
--- /dev/null
+++ b/xs/src/libslic3r/GCode/PreviewData.cpp
@@ -0,0 +1,402 @@
+#include "Analyzer.hpp"
+#include "PreviewData.hpp"
+
+namespace Slic3r {
+
+const GCodePreviewData::Color GCodePreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f);
+
+GCodePreviewData::Color::Color()
+{
+    rgba[0] = 1.0f;
+    rgba[1] = 1.0f;
+    rgba[2] = 1.0f;
+    rgba[3] = 1.0f;
+}
+
+GCodePreviewData::Color::Color(float r, float g, float b, float a)
+{
+    rgba[0] = r;
+    rgba[1] = g;
+    rgba[2] = b;
+    rgba[3] = a;
+}
+
+std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const
+{
+    std::vector<unsigned char> ret;
+    for (unsigned int i = 0; i < 4; ++i)
+    {
+        ret.push_back((unsigned char)(255.0f * rgba[i]));
+    }
+    return ret;
+}
+
+GCodePreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths)
+    : z(z)
+    , paths(paths)
+{
+}
+
+GCodePreviewData::Travel::Polyline::Polyline(EType type, EDirection direction, float feedrate, unsigned int extruder_id, const Polyline3& polyline)
+    : type(type)
+    , direction(direction)
+    , feedrate(feedrate)
+    , extruder_id(extruder_id)
+    , polyline(polyline)
+{
+}
+
+const GCodePreviewData::Color GCodePreviewData::Range::Default_Colors[Colors_Count] =
+{
+    Color(0.043f, 0.173f, 0.478f, 1.0f),
+    Color(0.075f, 0.349f, 0.522f, 1.0f),
+    Color(0.110f, 0.533f, 0.569f, 1.0f),
+    Color(0.016f, 0.839f, 0.059f, 1.0f),
+    Color(0.667f, 0.949f, 0.000f, 1.0f),
+    Color(0.988f, 0.975f, 0.012f, 1.0f),
+    Color(0.961f, 0.808f, 0.039f, 1.0f),
+    Color(0.890f, 0.533f, 0.125f, 1.0f),
+    Color(0.820f, 0.408f, 0.188f, 1.0f),
+    Color(0.761f, 0.322f, 0.235f, 1.0f)
+};
+
+GCodePreviewData::Range::Range()
+{
+    reset();
+}
+
+void GCodePreviewData::Range::reset()
+{
+    min = FLT_MAX;
+    max = -FLT_MAX;
+}
+
+bool GCodePreviewData::Range::empty() const
+{
+    return min == max;
+}
+
+void GCodePreviewData::Range::update_from(float value)
+{
+    min = std::min(min, value);
+    max = std::max(max, value);
+}
+
+void GCodePreviewData::Range::set_from(const Range& other)
+{
+    min = other.min;
+    max = other.max;
+}
+
+float GCodePreviewData::Range::step_size() const
+{
+    return (max - min) / (float)Colors_Count;
+}
+
+const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at_max() const
+{
+    return colors[Colors_Count - 1];
+}
+
+const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at(float value) const
+{
+    return empty() ? get_color_at_max() : colors[clamp((unsigned int)0, Colors_Count - 1, (unsigned int)((value - min) / step_size()))];
+}
+
+GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
+    : text(text)
+    , color(color)
+{
+}
+
+const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[Num_Extrusion_Roles] =
+{
+    Color(0.0f, 0.0f, 0.0f, 1.0f),   // erNone
+    Color(1.0f, 0.0f, 0.0f, 1.0f),   // erPerimeter
+    Color(0.0f, 1.0f, 0.0f, 1.0f),   // erExternalPerimeter
+    Color(0.0f, 0.0f, 1.0f, 1.0f),   // erOverhangPerimeter
+    Color(1.0f, 1.0f, 0.0f, 1.0f),   // erInternalInfill
+    Color(1.0f, 0.0f, 1.0f, 1.0f),   // erSolidInfill
+    Color(0.0f, 1.0f, 1.0f, 1.0f),   // erTopSolidInfill
+    Color(0.5f, 0.5f, 0.5f, 1.0f),   // erBridgeInfill
+    Color(1.0f, 1.0f, 1.0f, 1.0f),   // erGapFill
+    Color(0.5f, 0.0f, 0.0f, 1.0f),   // erSkirt
+    Color(0.0f, 0.5f, 0.0f, 1.0f),   // erSupportMaterial
+    Color(0.0f, 0.0f, 0.5f, 1.0f),   // erSupportMaterialInterface
+    Color(0.7f, 0.89f, 0.67f, 1.0f), // erWipeTower
+    Color(0.0f, 0.0f, 0.0f, 1.0f)    // erMixed
+};
+
+// todo: merge with Slic3r::ExtrusionRole2String() from GCode.cpp
+const std::string GCodePreviewData::Extrusion::Default_Extrusion_Role_Names[Num_Extrusion_Roles]
+{
+    "None",
+    "Perimeter",
+    "External perimeter",
+    "Overhang perimeter",
+    "Internal infill",
+    "Solid infill",
+    "Top solid infill",
+    "Bridge infill",
+    "Gap fill",
+    "Skirt",
+    "Support material",
+    "Support material interface",
+    "Wipe tower",
+    "Mixed"
+};
+
+const GCodePreviewData::Extrusion::EViewType GCodePreviewData::Extrusion::Default_View_Type = GCodePreviewData::Extrusion::FeatureType;
+
+void GCodePreviewData::Extrusion::set_default()
+{
+    view_type = Default_View_Type;
+
+    ::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, Num_Extrusion_Roles * sizeof(Color));
+    ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+    ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+    ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+
+    for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i)
+    {
+        role_names[i] = Default_Extrusion_Role_Names[i];
+    }
+
+    role_flags = 0;
+    for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i)
+    {
+        role_flags |= 1 << i;
+    }
+}
+
+bool GCodePreviewData::Extrusion::is_role_flag_set(ExtrusionRole role) const
+{
+    return is_role_flag_set(role_flags, role);
+}
+
+bool GCodePreviewData::Extrusion::is_role_flag_set(unsigned int flags, ExtrusionRole role)
+{
+    return GCodeAnalyzer::is_valid_extrusion_role(role) && (flags & (1 << (role - erPerimeter))) != 0;
+}
+
+const float GCodePreviewData::Travel::Default_Width = 0.075f;
+const float GCodePreviewData::Travel::Default_Height = 0.075f;
+const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
+{
+    Color(0.0f, 0.0f, 0.75f, 1.0f), // Move
+    Color(0.0f, 0.75f, 0.0f, 1.0f), // Extrude
+    Color(0.75f, 0.0f, 0.0f, 1.0f), // Retract
+};
+
+void GCodePreviewData::Travel::set_default()
+{
+    width = Default_Width;
+    height = Default_Height;
+    ::memcpy((void*)type_colors, (const void*)Default_Type_Colors, Num_Types * sizeof(Color));
+    is_visible = false;
+}
+
+const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
+
+GCodePreviewData::Retraction::Position::Position(const Point3& position, float width, float height)
+    : position(position)
+    , width(width)
+    , height(height)
+{
+}
+
+void GCodePreviewData::Retraction::set_default()
+{
+    color = Default_Color;
+    is_visible = false;
+}
+
+void GCodePreviewData::Shell::set_default()
+{
+    is_visible = false;
+}
+
+GCodePreviewData::GCodePreviewData()
+{
+    set_default();
+}
+
+void GCodePreviewData::set_default()
+{
+    extrusion.set_default();
+    travel.set_default();
+    retraction.set_default();
+    unretraction.set_default();
+    shell.set_default();
+}
+
+void GCodePreviewData::reset()
+{
+    extrusion.layers.clear();
+    travel.polylines.clear();
+    retraction.positions.clear();
+    unretraction.positions.clear();
+}
+
+const GCodePreviewData::Color& GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
+{
+    return extrusion.role_colors[role];
+}
+
+const GCodePreviewData::Color& GCodePreviewData::get_extrusion_height_color(float height) const
+{
+    return extrusion.ranges.height.get_color_at(height);
+}
+
+const GCodePreviewData::Color& GCodePreviewData::get_extrusion_width_color(float width) const
+{
+    return extrusion.ranges.width.get_color_at(width);
+}
+
+const GCodePreviewData::Color& GCodePreviewData::get_extrusion_feedrate_color(float feedrate) const
+{
+    return extrusion.ranges.feedrate.get_color_at(feedrate);
+}
+
+void GCodePreviewData::set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha)
+{
+    for (unsigned int i = 0; i < Extrusion::Num_Extrusion_Roles; ++i)
+    {
+        if (role_name == extrusion.role_names[i])
+        {
+            extrusion.role_colors[i] = Color(red, green, blue, alpha);
+            break;
+        }
+    }
+}
+
+void GCodePreviewData::set_extrusion_paths_colors(const std::vector<std::string>& colors)
+{
+    unsigned int size = (unsigned int)colors.size();
+
+    if (size % 2 != 0)
+        return;
+
+    for (unsigned int i = 0; i < size; i += 2)
+    {
+        const std::string& color_str = colors[i + 1];
+
+        if (color_str.size() == 6)
+        {
+            bool valid = true;
+            for (int c = 0; c < 6; ++c)
+            {
+                if (::isxdigit(color_str[c]) == 0)
+                {
+                    valid = false;
+                    break;
+                }
+            }
+
+            if (valid)
+            {
+                unsigned int color;
+                std::stringstream ss;
+                ss << std::hex << color_str;
+                ss >> color;
+
+                float den = 1.0f / 255.0f;
+
+                float r = (float)((color & 0xFF0000) >> 16) * den;
+                float g = (float)((color & 0x00FF00) >> 8) * den;
+                float b = (float)(color & 0x0000FF) * den;
+
+                this->set_extrusion_role_color(colors[i], r, g, b, 1.0f);
+            }
+        }
+    }
+}
+
+std::string GCodePreviewData::get_legend_title() const
+{
+    switch (extrusion.view_type)
+    {
+    case Extrusion::FeatureType:
+        return "Feature type";
+    case Extrusion::Height:
+        return "Height (mm)";
+    case Extrusion::Width:
+        return "Width (mm)";
+    case Extrusion::Feedrate:
+        return "Speed (mm/s)";
+    case Extrusion::Tool:
+        return "Tool";
+    }
+
+    return "";
+}
+
+GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector<float>& tool_colors) const
+{
+    struct Helper
+    {
+        static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor)
+        {
+            list.reserve(Range::Colors_Count);
+            float step = range.step_size();
+            for (unsigned int i = 0; i < Range::Colors_Count; ++i)
+            {
+                char buf[32];
+                sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step));
+                list.emplace_back(buf, range.colors[i]);
+            }
+        }
+    };
+
+    LegendItemsList items;
+
+    switch (extrusion.view_type)
+    {
+    case Extrusion::FeatureType:
+        {
+            items.reserve(erMixed - erPerimeter + 1);
+            for (unsigned int i = (unsigned int)erPerimeter; i < (unsigned int)erMixed; ++i)
+            {
+                items.emplace_back(extrusion.role_names[i], extrusion.role_colors[i]);
+            }
+
+            break;
+        }
+    case Extrusion::Height:
+        {
+            Helper::FillListFromRange(items, extrusion.ranges.height, 3, 1.0f);            
+            break;
+        }
+    case Extrusion::Width:
+        {
+            Helper::FillListFromRange(items, extrusion.ranges.width, 3, 1.0f);
+            break;
+        }
+    case Extrusion::Feedrate:
+        {
+            Helper::FillListFromRange(items, extrusion.ranges.feedrate, 0, 1.0f);
+            break;
+        }
+    case Extrusion::Tool:
+        {
+            unsigned int tools_colors_count = tool_colors.size() / 4;
+            items.reserve(tools_colors_count);
+            for (unsigned int i = 0; i < tools_colors_count; ++i)
+            {
+                char buf[32];
+                sprintf(buf, "Extruder %d", i + 1);
+
+                GCodePreviewData::Color color;
+                ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
+
+                items.emplace_back(buf, color);
+            }
+
+            break;
+        }
+    }
+
+    return items;
+}
+
+} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/xs/src/libslic3r/GCode/PreviewData.hpp
new file mode 100644
index 000000000..59d87a360
--- /dev/null
+++ b/xs/src/libslic3r/GCode/PreviewData.hpp
@@ -0,0 +1,204 @@
+#ifndef slic3r_GCode_PreviewData_hpp_
+#define slic3r_GCode_PreviewData_hpp_
+
+#include "../libslic3r.h"
+#include "../ExtrusionEntity.hpp"
+
+#include "Point.hpp"
+
+namespace Slic3r {
+
+class GCodePreviewData
+{
+public:
+    struct Color
+    {
+        float rgba[4];
+
+        Color();
+        Color(float r, float g, float b, float a);
+
+        std::vector<unsigned char> as_bytes() const;
+
+        static const Color Dummy;
+    };
+
+    struct Range
+    {
+        static const unsigned int Colors_Count = 10;
+        static const Color Default_Colors[Colors_Count];
+
+        Color colors[Colors_Count];
+        float min;
+        float max;
+
+        Range();
+
+        void reset();
+        bool empty() const;
+        void update_from(float value);
+        void set_from(const Range& other);
+        float step_size() const;
+
+        const Color& get_color_at(float value) const;
+        const Color& get_color_at_max() const;
+    };
+
+    struct LegendItem
+    {
+        std::string text;
+        Color color;
+
+        LegendItem(const std::string& text, const Color& color);
+    };
+
+    typedef std::vector<LegendItem> LegendItemsList;
+
+    struct Extrusion
+    {
+        enum EViewType : unsigned char
+        {
+            FeatureType,
+            Height,
+            Width,
+            Feedrate,
+            Tool,
+            Num_View_Types
+        };
+
+        static const unsigned int Num_Extrusion_Roles = (unsigned int)erMixed + 1;
+        static const Color Default_Extrusion_Role_Colors[Num_Extrusion_Roles];
+        static const std::string Default_Extrusion_Role_Names[Num_Extrusion_Roles];
+        static const EViewType Default_View_Type;
+
+        struct Ranges
+        {
+            Range height;
+            Range width;
+            Range feedrate;
+        };
+
+        struct Layer
+        {
+            float z;
+            ExtrusionPaths paths;
+
+            Layer(float z, const ExtrusionPaths& paths);
+        };
+
+        typedef std::vector<Layer> LayersList;
+
+        EViewType view_type;
+        Color role_colors[Num_Extrusion_Roles];
+        std::string role_names[Num_Extrusion_Roles];
+        Ranges ranges;
+        LayersList layers;
+        unsigned int role_flags;
+
+        void set_default();
+        bool is_role_flag_set(ExtrusionRole role) const;
+
+        static bool is_role_flag_set(unsigned int flags, ExtrusionRole role);
+    };
+
+    struct Travel
+    {
+        enum EType : unsigned char
+        {
+            Move,
+            Extrude,
+            Retract,
+            Num_Types
+        };
+
+        static const float Default_Width;
+        static const float Default_Height;
+        static const Color Default_Type_Colors[Num_Types];
+
+        struct Polyline
+        {
+            enum EDirection
+            {
+                Vertical,
+                Generic,
+                Num_Directions
+            };
+
+            EType type;
+            EDirection direction;
+            float feedrate;
+            unsigned int extruder_id;
+            Polyline3 polyline;
+
+            Polyline(EType type, EDirection direction, float feedrate, unsigned int extruder_id, const Polyline3& polyline);
+        };
+
+        typedef std::vector<Polyline> PolylinesList;
+
+        PolylinesList polylines;
+        float width;
+        float height;
+        Color type_colors[Num_Types];
+        bool is_visible;
+
+        void set_default();
+    };
+
+    struct Retraction
+    {
+        static const Color Default_Color;
+
+        struct Position
+        {
+            Point3 position;
+            float width;
+            float height;
+
+            Position(const Point3& position, float width, float height);
+        };
+
+        typedef std::vector<Position> PositionsList;
+
+        PositionsList positions;
+        Color color;
+        bool is_visible;
+
+        void set_default();
+    };
+
+    struct Shell
+    {
+        bool is_visible;
+
+        void set_default();
+    };
+
+    Extrusion extrusion;
+    Travel travel;
+    Retraction retraction;
+    Retraction unretraction;
+    Shell shell;
+
+    GCodePreviewData();
+
+    void set_default();
+    void reset();
+
+    const Color& get_extrusion_role_color(ExtrusionRole role) const;
+    const Color& get_extrusion_height_color(float height) const;
+    const Color& get_extrusion_width_color(float width) const;
+    const Color& get_extrusion_feedrate_color(float feedrate) const;
+
+    void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
+    void set_extrusion_paths_colors(const std::vector<std::string>& colors);
+
+    std::string get_legend_title() const;
+    LegendItemsList get_legend_items(const std::vector<float>& tool_colors) const;
+};
+
+GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2);
+GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color);
+
+} // namespace Slic3r
+
+#endif /* slic3r_GCode_PreviewData_hpp_ */