From 584b37513f54d625f0f3a7490cd63ce48f83a95f Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 19 Sep 2019 12:30:16 +0200
Subject: [PATCH 01/44] Implemented possibility to create "shape" as an object
 (#2865)

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 147 +++++++++++++++++++++++-------
 src/slic3r/GUI/GUI_ObjectList.hpp |   7 +-
 src/slic3r/GUI/Plater.cpp         |   5 +
 src/slic3r/GUI/Plater.hpp         |   1 +
 4 files changed, 126 insertions(+), 34 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 49f0f6fa2..116f99eb8 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -279,6 +279,7 @@ void ObjectList::create_popup_menus()
     create_part_popupmenu(&m_menu_part);
     create_sla_object_popupmenu(&m_menu_sla_object);
     create_instance_popupmenu(&m_menu_instance);
+    create_default_popupmenu(&m_menu_default);
 }
 
 void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(nullptr)*/)
@@ -783,18 +784,23 @@ void ObjectList::OnChar(wxKeyEvent& event)
 
 void ObjectList::OnContextMenu(wxDataViewEvent&)
 {
-    list_manipulation();
+    list_manipulation(true);
 }
 
-void ObjectList::list_manipulation()
+void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
 {
     wxDataViewItem item;
     wxDataViewColumn* col = nullptr;
     const wxPoint pt = get_mouse_position_in_control();
     HitTest(pt, item, col);
 
-    if (!item || col == nullptr) {
-        return;
+    if (!item) {
+        if (col == nullptr)
+            return;
+        if (evt_context_menu) {
+            show_context_menu(evt_context_menu);
+            return;
+        }
     }
 
     const wxString title = col->GetTitle();
@@ -802,7 +808,7 @@ void ObjectList::list_manipulation()
     if (title == " ")
         toggle_printable_state(item);
     else if (title == _("Editing"))
-        show_context_menu();
+        show_context_menu(evt_context_menu);
     else if (title == _("Name"))
     {
         int obj_idx, vol_idx;
@@ -818,7 +824,7 @@ void ObjectList::list_manipulation()
 #endif //__WXMSW__
 }
 
-void ObjectList::show_context_menu()
+void ObjectList::show_context_menu(const bool evt_context_menu)
 {
     if (multiple_selection())
     {
@@ -831,22 +837,26 @@ void ObjectList::show_context_menu()
     }
 
     const auto item = GetSelection();
+    wxMenu* menu {nullptr};
     if (item)
     {
         const ItemType type = m_objects_model->GetItemType(item);
         if (!(type & (itObject | itVolume | itLayer | itInstance)))
             return;
 
-        wxMenu* menu = type & itInstance ? &m_menu_instance :
+        menu = type & itInstance ? &m_menu_instance :
                        type & itLayer ? &m_menu_layer :
                        m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part :
                        printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
 
         if (!(type & itInstance))
             append_menu_item_settings(menu);
-
-        wxGetApp().plater()->PopupMenu(menu);
     }
+    else if (evt_context_menu)
+        menu = &m_menu_default;
+
+    if (menu)
+        wxGetApp().plater()->PopupMenu(menu);
 }
 
 void ObjectList::copy()
@@ -1286,13 +1296,16 @@ void ObjectList::show_settings(const wxDataViewItem settings_item)
 wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) {
     auto sub_menu = new wxMenu;
 
-    if (wxGetApp().get_mode() == comExpert) {
+    if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) {
     append_menu_item(sub_menu, wxID_ANY, _(L("Load")) + " " + dots, "",
         [this, type](wxCommandEvent&) { load_subobject(type); }, "", menu);
     sub_menu->AppendSeparator();
     }
 
-    for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) {
+    for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
+    {
+        if (type == ModelVolumeType::INVALID && item == "Slab")
+            continue;
         append_menu_item(sub_menu, wxID_ANY, _(item), "",
             [this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu);
     }
@@ -1579,6 +1592,12 @@ void ObjectList::create_instance_popupmenu(wxMenu*menu)
     }, m_menu_item_split_instances->GetId());
 }
 
+void ObjectList::create_default_popupmenu(wxMenu*menu)
+{
+    wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::INVALID);
+    append_submenu(menu, sub_menu, wxID_ANY, _(L("Add Shape")), "", "add_part");
+}
+
 wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
 {
     wxMenu *menu = new wxMenu;
@@ -1717,8 +1736,38 @@ void ObjectList::load_part( ModelObject* model_object,
 
 }
 
+static TriangleMesh create_mesh(const std::string& type_name, const BoundingBoxf3& bb)
+{
+    TriangleMesh mesh;
+
+    const double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
+
+    if (type_name == "Box")
+        // Sitting on the print bed, left front front corner at (0, 0).
+        mesh = make_cube(side, side, side);
+    else if (type_name == "Cylinder")
+        // Centered around 0, sitting on the print bed.
+        // The cylinder has the same volume as the box above.
+        mesh = make_cylinder(0.564 * side, side);
+    else if (type_name == "Sphere")
+        // Centered around 0, half the sphere below the print bed, half above.
+        // The sphere has the same volume as the box above.
+        mesh = make_sphere(0.62 * side, PI / 18);
+    else if (type_name == "Slab")
+        // Sitting on the print bed, left front front corner at (0, 0).
+        mesh = make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5);
+    mesh.repair();
+
+    return mesh;
+}
+
 void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
 {
+    if (type == ModelVolumeType::INVALID) {
+        load_shape_object(type_name);
+        return;
+    }
+
     const int obj_idx = get_selected_obj_idx();
     if (obj_idx < 0) 
         return;
@@ -1741,26 +1790,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
     // Bounding box of the selected instance in world coordinate system including the translation, without modifiers.
     BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx);
 
-    const wxString name = _(L("Generic")) + "-" + _(type_name);
-    TriangleMesh mesh;
-
-    double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
-
-    if (type_name == "Box")
-        // Sitting on the print bed, left front front corner at (0, 0).
-        mesh = make_cube(side, side, side);
-    else if (type_name == "Cylinder")
-        // Centered around 0, sitting on the print bed.
-        // The cylinder has the same volume as the box above.
-        mesh = make_cylinder(0.564 * side, side);
-    else if (type_name == "Sphere")
-        // Centered around 0, half the sphere below the print bed, half above.
-        // The sphere has the same volume as the box above.
-        mesh = make_sphere(0.62 * side, PI / 18);
-    else if (type_name == "Slab")
-        // Sitting on the print bed, left front front corner at (0, 0).
-        mesh = make_cube(instance_bb.size().x()*1.5, instance_bb.size().y()*1.5, instance_bb.size().z()*0.5);
-    mesh.repair();
+    TriangleMesh mesh = create_mesh(type_name, instance_bb);
     
 	// Mesh will be centered when loading.
     ModelVolume *new_volume = model_object.add_volume(std::move(mesh));
@@ -1782,6 +1812,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
         new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset);
     }
 
+    const wxString name = _(L("Generic")) + "-" + _(type_name);
     new_volume->name = into_u8(name);
     // set a default extruder value, since user can't add it manually
     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
@@ -1799,6 +1830,57 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
 #endif //no __WXOSX__ //__WXMSW__
 }
 
+void ObjectList::load_shape_object(const std::string& type_name)
+{
+    const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
+    assert(selection.get_object_idx() == -1); // Add nothing is something is selected on 3DScene
+    if (selection.get_object_idx() != -1)
+        return;
+
+    const int obj_idx = m_objects->size();
+    if (obj_idx < 0)
+        return;
+
+    take_snapshot(_(L("Add Shape")));
+
+    // Create mesh
+    BoundingBoxf3 bb;
+    TriangleMesh mesh = create_mesh(type_name, bb);
+
+    // Add mesh to model as a new object
+    Model& model = wxGetApp().plater()->model();
+    const wxString name = _(L("Shape")) + "-" + _(type_name);
+
+#ifdef _DEBUG
+    check_model_ids_validity(model);
+#endif /* _DEBUG */
+
+    std::vector<size_t> object_idxs;
+    ModelObject* new_object = model.add_object();
+    new_object->name = into_u8(name);
+    new_object->add_instance(); // each object should have at list one instance
+
+    ModelVolume* new_volume = new_object->add_volume(mesh);
+    new_volume->name = into_u8(name);
+    // set a default extruder value, since user can't add it manually
+    new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
+    new_object->invalidate_bounding_box();
+
+    const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
+    new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));
+
+    object_idxs.push_back(model.objects.size() - 1);
+#ifdef _DEBUG
+    check_model_ids_validity(model);
+#endif /* _DEBUG */
+
+    paste_objects_into_list(object_idxs);
+
+#ifdef _DEBUG
+    check_model_ids_validity(model);
+#endif /* _DEBUG */
+}
+
 void ObjectList::del_object(const int obj_idx)
 {
     wxGetApp().plater()->delete_object_from_model(obj_idx);
@@ -3585,7 +3667,8 @@ void ObjectList::msw_rescale()
                                       &m_menu_part, 
                                       &m_menu_sla_object, 
                                       &m_menu_instance, 
-                                      &m_menu_layer })
+                                      &m_menu_layer,
+                                      &m_menu_default})
         msw_rescale_menu(menu);
 
     Layout();
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 13d1106fc..4dd618a90 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -132,6 +132,7 @@ private:
     MenuWithSeparators  m_menu_sla_object;
     MenuWithSeparators  m_menu_instance;
     MenuWithSeparators  m_menu_layer;
+    MenuWithSeparators  m_menu_default;
     wxMenuItem* m_menu_item_settings { nullptr };
     wxMenuItem* m_menu_item_split_instances { nullptr };
 
@@ -208,7 +209,7 @@ public:
     void                set_tooltip_for_item(const wxPoint& pt);
 
     void                selection_changed();
-    void                show_context_menu();
+    void                show_context_menu(const bool evt_context_menu);
 #ifndef __WXOSX__
     void                key_event(wxKeyEvent& event);
 #endif /* __WXOSX__ */
@@ -240,6 +241,7 @@ public:
     void                create_sla_object_popupmenu(wxMenu*menu);
     void                create_part_popupmenu(wxMenu*menu);
     void                create_instance_popupmenu(wxMenu*menu);
+    void                create_default_popupmenu(wxMenu *menu);
     wxMenu*             create_settings_popupmenu(wxMenu *parent_menu);
     void                create_freq_settings_popupmenu(wxMenu *parent_menu, const bool is_object_settings = true);
 
@@ -248,6 +250,7 @@ public:
     void                load_subobject(ModelVolumeType type);
     void                load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
 	void                load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
+    void                load_shape_object(const std::string &type_name);
     void                del_object(const int obj_idx);
     void                del_subobject_item(wxDataViewItem& item);
     void                del_settings_from_config(const wxDataViewItem& parent_item);
@@ -362,7 +365,7 @@ private:
 //    void OnChar(wxKeyEvent& event);
 #endif /* __WXOSX__ */
     void OnContextMenu(wxDataViewEvent &event);
-    void list_manipulation();
+    void list_manipulation(bool evt_context_menu = false);
 
     void OnBeginDrag(wxDataViewEvent &event);
     void OnDropPossible(wxDataViewEvent &event);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 0dc7519e8..4ba54bc6c 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -4877,6 +4877,11 @@ GLCanvas3D* Plater::canvas3D()
     return p->view3D->get_canvas3d();
 }
 
+BoundingBoxf Plater::bed_shape_bb() const
+{
+    return p->bed_shape_bb();
+}
+
 PrinterTechnology Plater::printer_technology() const
 {
     return p->printer_technology;
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 0dc298566..26dcb5ac3 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -225,6 +225,7 @@ public:
     int get_selected_object_idx();
     bool is_single_full_object_selection() const;
     GLCanvas3D* canvas3D();
+    BoundingBoxf bed_shape_bb() const;
 
     PrinterTechnology   printer_technology() const;
     void                set_printer_technology(PrinterTechnology printer_technology);

From 355a7d6a8c5a503a0cbdad16ab58e01d27c5ac63 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 19 Sep 2019 14:01:17 +0200
Subject: [PATCH 02/44] Fixed empty filename when trying to export a plate
 where no object is selected and no object is printable

---
 src/slic3r/GUI/Plater.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 0dc7519e8..7457368ce 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -2518,6 +2518,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
         if (output_file.empty())
             // Find the file name of the first printable object.
             output_file = this->model.propose_export_file_name_and_path();
+
+        if (output_file.empty() && !model.objects.empty())
+            // Find the file name of the first object.
+            output_file = this->model.objects[0]->get_export_filename();
     }
 
     wxString dlg_title;

From ddf37d3b01d8c19dc004a423b25c18ab1f7cddc4 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 19 Sep 2019 14:11:56 +0200
Subject: [PATCH 03/44] Fix under OSX for
 https://github.com/prusa3d/PrusaSlicer/commit/584b37513f54d625f0f3a7490cd63ce48f83a95f

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 116f99eb8..47be4b5eb 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -794,15 +794,26 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
     const wxPoint pt = get_mouse_position_in_control();
     HitTest(pt, item, col);
 
+    /* Note: Under OSX right click doesn't send "selection changed" event.
+     * It means that Selection() will be return still previously selected item.
+     * Thus under OSX we should force UnselectAll(), when item and col are nullptr,
+     * and select new item otherwise.
+     */
+
     if (!item) {
-        if (col == nullptr)
-            return;
+        if (wxOSX && col == nullptr)
+            UnselectAll();
         if (evt_context_menu) {
             show_context_menu(evt_context_menu);
             return;
         }
     }
 
+    if (wxOSX && item && col) {
+        UnselectAll();
+        Select(item);
+    }
+
     const wxString title = col->GetTitle();
 
     if (title == " ")

From 2414dab85a4bc53f27055e93954a7b29c5729855 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 19 Sep 2019 14:31:50 +0200
Subject: [PATCH 04/44] Fix of perl bindings

---
 xs/xsp/ExtrusionEntityCollection.xsp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp
index a868ea6bb..1f16ae3d4 100644
--- a/xs/xsp/ExtrusionEntityCollection.xsp
+++ b/xs/xsp/ExtrusionEntityCollection.xsp
@@ -31,13 +31,11 @@
     ExtrusionEntityCollection* flatten()
         %code{%
             RETVAL = new ExtrusionEntityCollection();
-            THIS->flatten(RETVAL);
+            *RETVAL = THIS->flatten();
         %};
     double min_mm3_per_mm();
     bool empty()
         %code{% RETVAL = THIS->entities.empty(); %};
-    std::vector<size_t> orig_indices()
-        %code{% RETVAL = THIS->orig_indices; %};
     Polygons polygons_covered_by_width();
     Polygons polygons_covered_by_spacing();
 %{

From 29f3c938e6c6409be3b1a08b8dc8fca049b2379d Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 19 Sep 2019 15:28:36 +0200
Subject: [PATCH 05/44] Fix of SPE-1026 (Height range value works differently
 if you press enter and if you leave textbox)

Related to GH #2946
---
 src/slic3r/GUI/GUI_ObjectLayers.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp
index 9053cdd42..d209214ae 100644
--- a/src/slic3r/GUI/GUI_ObjectLayers.cpp
+++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp
@@ -284,6 +284,9 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
                wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
 {
     this->SetFont(wxGetApp().normal_font());
+
+    // Reset m_enter_pressed flag to _false_, when value is editing
+    this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId());
     
     this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
     {
@@ -307,7 +310,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
         if (!m_enter_pressed) {
 #ifndef __WXGTK__
             /* Update data for next editor selection.
-             * But under GTK it lucks like there is no information about selected control at e.GetWindow(),
+             * But under GTK it looks like there is no information about selected control at e.GetWindow(),
              * so we'll take it from wxEVT_LEFT_DOWN event
              * */
             LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());

From 6ce1011a09943d084f82e96156805fae7fb6a24b Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 19 Sep 2019 17:16:37 +0200
Subject: [PATCH 06/44] Slic3r -> PrusaSlicer in config snapshot dialog.

---
 src/slic3r/GUI/ConfigSnapshotDialog.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp
index 836a0a4d3..c89e4895e 100644
--- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp
+++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp
@@ -42,7 +42,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
         text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")";
     text += "</b></font><br>";
     // End of row header.
-    text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
+    text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
     text += _(L("print")) + ": " + snapshot.print + "<br>";
     text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
     text += _(L("printer")) + ": " + snapshot.printer + "<br>";
@@ -50,9 +50,9 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
     bool compatible = true;
     for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
         text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() + 
-				", " + _(L("min slic3r version")) + ": " + vc.version.min_slic3r_version.to_string();
+				", " + _(L("min PrusaSlicer version")) + ": " + vc.version.min_slic3r_version.to_string();
         if (vc.version.max_slic3r_version != Semver::inf())
-            text += ", " + _(L("max slic3r version")) + ": " + vc.version.max_slic3r_version.to_string();
+            text += ", " + _(L("max PrusaSlicer version")) + ": " + vc.version.max_slic3r_version.to_string();
         text += "<br>";
         for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
             text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";

From 9e09c52ab008f6e8346972f0591a60e248d78ddb Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 20 Sep 2019 09:53:35 +0200
Subject: [PATCH 07/44] #2948 - Max zoom takes in account custom bed model size

---
 src/slic3r/GUI/GLCanvas3D.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index edb9c7830..08e47f30b 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3372,7 +3372,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
 void GLCanvas3D::set_camera_zoom(double zoom)
 {
     const Size& cnv_size = get_canvas_size();
-    m_camera.set_zoom(zoom, _max_bounding_box(false, false), cnv_size.get_width(), cnv_size.get_height());
+    m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height());
     m_dirty = true;
 }
 

From 8aaff083557a79a041ee17f1d6879a5574e62599 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 20 Sep 2019 09:57:27 +0200
Subject: [PATCH 08/44] Added Possibility to create "shape" as an independent
 object from 3dScene, using right click on empty place

---
 src/slic3r/GUI/GLCanvas3D.cpp | 12 ++++-
 src/slic3r/GUI/GLCanvas3D.hpp |  5 +-
 src/slic3r/GUI/Plater.cpp     | 91 ++++++++++++++++++++---------------
 3 files changed, 66 insertions(+), 42 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index edb9c7830..6d069df29 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1094,7 +1094,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
 wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
-wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
+wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
@@ -3012,15 +3012,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                     wxGetApp().obj_manipul()->set_dirty();
                     // forces a frame render to update the view before the context menu is shown
                     render();
-
+/*  #et_FIXME
                     Vec2d logical_pos = pos.cast<double>();
 #if ENABLE_RETINA_GL
                     const float factor = m_retina_helper->get_scale_factor();
                     logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
 #endif // ENABLE_RETINA_GL
                     post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos));
+*/
                 }
             }
+            // #et_FIXME
+            Vec2d logical_pos = pos.cast<double>();
+#if ENABLE_RETINA_GL
+            const float factor = m_retina_helper->get_scale_factor();
+            logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
+#endif // ENABLE_RETINA_GL
+            post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, {logical_pos, m_hover_volume_idxs.empty()}));
         }
 
         mouse_up_cleanup();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index b15402a52..e6b10e9ca 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -71,6 +71,9 @@ public:
 wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
 
 using Vec2dEvent = Event<Vec2d>;
+// #et_FIXME : RBtnEvent is used instead of Vec2dEvent on EVT_GLCANVAS_RIGHT_CLICK
+// _bool_ value is used as a indicator of selection in the 3DScene
+using RBtnEvent = Event<std::pair<Vec2d, bool>>;
 template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
 
 using Vec3dEvent = Event<Vec3d>;
@@ -78,7 +81,7 @@ template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
 
 wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
-wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
+wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 524034ea9..18bc08d27 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1340,6 +1340,8 @@ struct Plater::priv
     MenuWithSeparators part_menu;
     // SLA-Object popup menu
     MenuWithSeparators sla_object_menu;
+    // Default popup menu (when nothing is selected on 3DScene)
+    MenuWithSeparators default_menu;
 
     // Removed/Prepended Items according to the view mode
     std::vector<wxMenuItem*> items_increase;
@@ -1879,7 +1881,7 @@ struct Plater::priv
     void on_action_layersediting(SimpleEvent&);
 
     void on_object_select(SimpleEvent&);
-    void on_right_click(Vec2dEvent&);
+    void on_right_click(RBtnEvent&);
     void on_wipetower_moved(Vec3dEvent&);
     void on_wipetower_rotated(Vec3dEvent&);
     void on_update_geometry(Vec3dsEvent<2>&);
@@ -3446,57 +3448,66 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
     selection_changed();
 }
 
-void Plater::priv::on_right_click(Vec2dEvent& evt)
+void Plater::priv::on_right_click(RBtnEvent& evt)
 {
     int obj_idx = get_selected_object_idx();
+
+    wxMenu* menu = nullptr;
+
     if (obj_idx == -1)
-        return;
-
-    wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu :
-                   get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
-                   &object_menu : &part_menu;
-
-    sidebar->obj_list()->append_menu_item_settings(menu);
-
-    if (printer_technology != ptSLA)
-        sidebar->obj_list()->append_menu_item_change_extruder(menu);
-
-    if (menu != &part_menu)
+        menu = &default_menu;
+    else
     {
-        /* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
-         * Suppress to show those items for a Simple mode
-         */
-        const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
-        if (wxGetApp().get_mode() == comSimple) {
-            if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
-            {
-                /* Detach an items from the menu, but don't delete them
-                 * so that they can be added back later
-                 * (after switching to the Advanced/Expert mode)
-                 */
-                menu->Remove(items_increase[id]);
-                menu->Remove(items_decrease[id]);
-                menu->Remove(items_set_number_of_copies[id]);
+        // If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
+        if (evt.data.second)
+            return; 
+
+        menu = printer_technology == ptSLA ? &sla_object_menu :
+               get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
+               &object_menu : &part_menu;
+
+        sidebar->obj_list()->append_menu_item_settings(menu);
+
+        if (printer_technology != ptSLA)
+            sidebar->obj_list()->append_menu_item_change_extruder(menu);
+
+        if (menu != &part_menu)
+        {
+            /* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
+             * Suppress to show those items for a Simple mode
+             */
+            const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
+            if (wxGetApp().get_mode() == comSimple) {
+                if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
+                {
+                    /* Detach an items from the menu, but don't delete them
+                     * so that they can be added back later
+                     * (after switching to the Advanced/Expert mode)
+                     */
+                    menu->Remove(items_increase[id]);
+                    menu->Remove(items_decrease[id]);
+                    menu->Remove(items_set_number_of_copies[id]);
+                }
             }
-        }
-        else {
-            if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
-            {
-                // Prepend items to the menu, if those aren't not there
-                menu->Prepend(items_set_number_of_copies[id]);
-                menu->Prepend(items_decrease[id]);
-                menu->Prepend(items_increase[id]);
+            else {
+                if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
+                {
+                    // Prepend items to the menu, if those aren't not there
+                    menu->Prepend(items_set_number_of_copies[id]);
+                    menu->Prepend(items_decrease[id]);
+                    menu->Prepend(items_increase[id]);
+                }
             }
         }
     }
 
-    if (q != nullptr) {
+    if (q != nullptr && menu) {
 #ifdef __linux__
         // For some reason on Linux the menu isn't displayed if position is specified
         // (even though the position is sane).
         q->PopupMenu(menu);
 #else
-        q->PopupMenu(menu, (int)evt.data.x(), (int)evt.data.y());
+        q->PopupMenu(menu, (int)evt.data.first.x(), (int)evt.data.first.y());
 #endif
     }
 }
@@ -3548,12 +3559,14 @@ bool Plater::priv::init_object_menu()
     init_common_menu(&part_menu, true);
     complit_init_part_menu();
 
+    sidebar->obj_list()->create_default_popupmenu(&default_menu);
+
     return true;
 }
 
 void Plater::priv::msw_rescale_object_menu()
 {
-    for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu })
+    for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu })
         msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
 }
 

From b241ba16ed403492dad83068e655fec1723305e3 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 20 Sep 2019 10:53:50 +0200
Subject: [PATCH 09/44] Fixed layer profile equality check for wipe tower
 validation (fixup of b43003d)

---
 src/libslic3r/Print.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index cbbbf1f1a..4d8482743 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1236,7 +1236,8 @@ std::string Print::validate() const
 
                     // The comparison of the profiles is not just about element-wise equality, some layers may not be
                     // explicitely included. Always remember z and height of last reference layer that in the vector
-                    // and compare to that.
+                    // and compare to that. In case some layers are in the vectors multiple times, only the last entry is
+                    // taken into account and compared.
                     size_t i = 0; // index into tested profile
                     size_t j = 0; // index into reference profile
                     coordf_t ref_z = -1.;
@@ -1244,8 +1245,12 @@ std::string Print::validate() const
                     coordf_t ref_height = -1.;
                     while (i < layer_height_profile.size()) {
                         coordf_t this_z = layer_height_profile[i];
+                        // find the last entry with this z
+                        while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z)
+                            i += 2;
+
                         coordf_t this_height = layer_height_profile[i+1];
-                        if (next_ref_z < this_z + EPSILON) {
+                        if (ref_height < -1. || next_ref_z < this_z + EPSILON) {
                             ref_z = next_ref_z;
                             do { // one layer can be in the vector several times
                                 ref_height = layer_height_profile_tallest[j+1];

From d66bf7e1e198b66feb78af9d7787490428995015 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 20 Sep 2019 11:19:06 +0200
Subject: [PATCH 10/44] Follow-up of 8aaff083557a79a041ee17f1d6879a5574e62599
 -> Do not show the new context menu when the user pans the scene + cleanup

---
 src/slic3r/GUI/GLCanvas3D.cpp | 13 +++----------
 src/slic3r/GUI/GLCanvas3D.hpp |  1 -
 2 files changed, 3 insertions(+), 11 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 21cd72a22..715d9f806 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3012,23 +3012,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                     wxGetApp().obj_manipul()->set_dirty();
                     // forces a frame render to update the view before the context menu is shown
                     render();
-/*  #et_FIXME
-                    Vec2d logical_pos = pos.cast<double>();
-#if ENABLE_RETINA_GL
-                    const float factor = m_retina_helper->get_scale_factor();
-                    logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
-#endif // ENABLE_RETINA_GL
-                    post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos));
-*/
                 }
             }
-            // #et_FIXME
             Vec2d logical_pos = pos.cast<double>();
 #if ENABLE_RETINA_GL
             const float factor = m_retina_helper->get_scale_factor();
             logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
 #endif // ENABLE_RETINA_GL
-            post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, {logical_pos, m_hover_volume_idxs.empty()}));
+            if (!m_mouse.dragging)
+                // do not post the event if the user is panning the scene
+                post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() }));
         }
 
         mouse_up_cleanup();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index e6b10e9ca..fb767360c 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -71,7 +71,6 @@ public:
 wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
 
 using Vec2dEvent = Event<Vec2d>;
-// #et_FIXME : RBtnEvent is used instead of Vec2dEvent on EVT_GLCANVAS_RIGHT_CLICK
 // _bool_ value is used as a indicator of selection in the 3DScene
 using RBtnEvent = Event<std::pair<Vec2d, bool>>;
 template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;

From 7e060f84bd534fb027bdc3a1f4b48cce912090bd Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 20 Sep 2019 11:30:29 +0200
Subject: [PATCH 11/44] Forcing of explicit SetWidth for the columns under OSX,
 as an attempt to fix a narrow column width on 4(5)K monitors under OSX

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 47be4b5eb..1d5d8f1a5 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -255,21 +255,30 @@ void ObjectList::create_objects_ctrl()
     EnableDropTarget(wxDF_UNICODETEXT);
 #endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
 
+    const int em = wxGetApp().em_unit();
+
     // column ItemName(Icon+Text) of the view control: 
     // And Icon can be consisting of several bitmaps
     AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(),
-        colName, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
+        colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
 
     // column PrintableProperty (Icon) of the view control:
-    AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, int(2 * wxGetApp().em_unit()),
+    AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, 3*em,
         wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
 
     // column Extruder of the view control:
     AppendColumn(create_objects_list_extruder_column(4));
 
     // column ItemEditing of the view control:
-    AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/,
+    AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em,
         wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
+
+    if (wxOSX)
+    {
+        GetColumn(colName)->SetWidth(20*em);
+        GetColumn(colPrint)->SetWidth(3*em);
+        GetColumn(colExtruder)->SetWidth(8*em);
+    }
 }
 
 void ObjectList::create_popup_menus()
@@ -822,12 +831,18 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
         show_context_menu(evt_context_menu);
     else if (title == _("Name"))
     {
-        int obj_idx, vol_idx;
-        get_selected_item_indexes(obj_idx, vol_idx, item);
+        if (wxOSX)
+            show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
 
-        if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0 && 
-            pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
-            fix_through_netfabb();
+        if (is_windows10())
+        {
+            int obj_idx, vol_idx;
+            get_selected_item_indexes(obj_idx, vol_idx, item);
+
+            if (get_mesh_errors_count(obj_idx, vol_idx) > 0 && 
+                pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
+                fix_through_netfabb();
+        }
     }
 
 #ifndef __WXMSW__

From 656569b0e9e836c628f85cabc16e6805382ac729 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 20 Sep 2019 16:45:43 +0200
Subject: [PATCH 12/44] Fix of
 https://github.com/prusa3d/PrusaSlicer/issues/2953

printf-like function argument mismatch: num was long, which was obfuscated by the auto keyword
---
 src/slic3r/GUI/Plater.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 18bc08d27..28474cac9 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -4336,14 +4336,14 @@ void Plater::set_number_of_copies(/*size_t num*/)
 
     ModelObject* model_object = p->model.objects[obj_idx];
 
-    const auto num = wxGetNumberFromUser( " ", _("Enter the number of copies:"),
+    const int num = wxGetNumberFromUser( " ", _("Enter the number of copies:"),
                                     _("Copies of the selected object"), model_object->instances.size(), 0, 1000, this );
     if (num < 0)
         return;
 
     Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num));
 
-    int diff = (int)num - (int)model_object->instances.size();
+    int diff = num - (int)model_object->instances.size();
     if (diff > 0)
         increase_instances(diff);
     else if (diff < 0)

From 927ad5776ce309ed4155e8b6a36500738304f2bb Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Fri, 20 Sep 2019 17:14:28 +0200
Subject: [PATCH 13/44] avrdude: Cherry-pick rev 1421 from upstream:

Submitted by Reinhard Max patch #8311: Add IPv6 support to the
-Pnet:host:port option * ser_posix.c (net_open):
Rewrite to use getaddrinfo() rather than gethostbyname()

Fix #2918
---
 src/avrdude/ChangeLog        |  9 ++++
 src/avrdude/avrdude.1        |  7 +++
 src/avrdude/configure.ac     |  2 +-
 src/avrdude/doc/avrdude.texi |  5 ++
 src/avrdude/ser_posix.c      | 99 +++++++++++++++++++++---------------
 5 files changed, 81 insertions(+), 41 deletions(-)

diff --git a/src/avrdude/ChangeLog b/src/avrdude/ChangeLog
index 975f52317..879fbf957 100644
--- a/src/avrdude/ChangeLog
+++ b/src/avrdude/ChangeLog
@@ -1,3 +1,12 @@
+2018-01-17  Joerg Wunsch <j.gnu@uriah.heep.sax.de>
+(cherry-picked)
+	Submitted by Reinhard Max
+	patch #8311: Add IPv6 support to the -Pnet:host:port option
+	* ser_posix.c (net_open): Rewrite to use getaddrinfo()
+	rather than gethostbyname()
+	* avrdude.1: Document IPv6 feature
+	* doc/avrdude.texi: (Dito)
+
 2016-05-10  Joerg Wunsch <j.gnu@uriah.heep.sax.de>
 
 	Submitted by Hannes Jochriem:
diff --git a/src/avrdude/avrdude.1 b/src/avrdude/avrdude.1
index 65fc7b1d6..47ca4edde 100644
--- a/src/avrdude/avrdude.1
+++ b/src/avrdude/avrdude.1
@@ -505,12 +505,19 @@ network connection to (TCP)
 on
 .Ar host
 is established.
+Square brackets may be placed around
+.Ar host
+to improve readability, for numeric IPv6 addresses (e.g.
+.Li net:[2001:db8::42]:1337 ) .
 The remote endpoint is assumed to be a terminal or console server
 that connects the network stream to a local serial port where the
 actual programmer has been attached to.
 The port is assumed to be properly configured, for example using a
 transparent 8-bit data connection without parity at 115200 Baud
 for a STK500.
+.Pp
+Note: The ability to handle IPv6 hostnames and addresses is limited to
+Posix systems (by now).
 .It Fl q
 Disable (or quell) output of the progress bar while reading or writing
 to the device.  Specify it a second time for even quieter operation.
diff --git a/src/avrdude/configure.ac b/src/avrdude/configure.ac
index a23a959f2..d14fc545f 100644
--- a/src/avrdude/configure.ac
+++ b/src/avrdude/configure.ac
@@ -214,7 +214,7 @@ AC_HEADER_TIME
 AC_CHECK_LIB([ws2_32], [puts])
 
 # Checks for library functions.
-AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep])
+AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep getaddrinfo])
 
 AC_MSG_CHECKING([for a Win32 HID libray])
 SAVED_LIBS="${LIBS}"
diff --git a/src/avrdude/doc/avrdude.texi b/src/avrdude/doc/avrdude.texi
index 6941389df..7062a9920 100644
--- a/src/avrdude/doc/avrdude.texi
+++ b/src/avrdude/doc/avrdude.texi
@@ -557,6 +557,9 @@ higher level protocol (as opposed to bit-bang style programmers),
 In this case, instead of trying to open a local device, a TCP
 network connection to (TCP) @var{port} on @var{host}
 is established.
+Square brackets may be placed around @var{host} to improve
+readability for numeric IPv6 addresses (e.g.
+@code{net:[2001:db8::42]:1337}).
 The remote endpoint is assumed to be a terminal or console server
 that connects the network stream to a local serial port where the
 actual programmer has been attached to.
@@ -564,6 +567,8 @@ The port is assumed to be properly configured, for example using a
 transparent 8-bit data connection without parity at 115200 Baud
 for a STK500.
 
+Note: The ability to handle IPv6 hostnames and addresses is limited to
+Posix systems (by now).
 
 @item -q
 Disable (or quell) output of the progress bar while reading or writing
diff --git a/src/avrdude/ser_posix.c b/src/avrdude/ser_posix.c
index 9992f78e3..dfa02f9fe 100644
--- a/src/avrdude/ser_posix.c
+++ b/src/avrdude/ser_posix.c
@@ -150,6 +150,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
   return 0;
 }
 
+#include "ac_cfg.h"
 
 // Timeout read & write variants
 // Additionally to the regular -1 on I/O error, they return -2 on timeout
@@ -221,23 +222,35 @@ ssize_t write_timeout(int fd, const void *buf, size_t count, long timeout)
 static int
 net_open(const char *port, union filedescriptor *fdp)
 {
-  char *hstr, *pstr, *end;
-  unsigned int pnum;
-  int fd;
-  struct sockaddr_in sockaddr;
-  struct hostent *hp;
+#ifdef HAVE_GETADDRINFO
+  char *hp, *hstr, *pstr;
+  int s, fd, ret = -1;
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
 
-  if ((hstr = strdup(port)) == NULL) {
+  if ((hstr = hp = strdup(port)) == NULL) {
     avrdude_message(MSG_INFO, "%s: net_open(): Out of memory!\n",
 	    progname);
     return -1;
   }
 
-  if (((pstr = strchr(hstr, ':')) == NULL) || (pstr == hstr)) {
+  /*
+   * As numeric IPv6 addresses use colons as separators, we need to
+   * look for the last colon here, which separates the port number or
+   * service name from the host or IP address.
+   */
+  if (((pstr = strrchr(hstr, ':')) == NULL) || (pstr == hstr)) {
     avrdude_message(MSG_INFO, "%s: net_open(): Mangled host:port string \"%s\"\n",
 	    progname, hstr);
-    free(hstr);
-    return -1;
+    goto error;
+  }
+
+  /*
+   * Remove brackets from the host part, if present.
+   */
+  if (*hstr == '[' && *(pstr-1) == ']') {
+    hstr++;
+    *(pstr-1) = '\0';
   }
 
   /*
@@ -245,43 +258,49 @@ net_open(const char *port, union filedescriptor *fdp)
    */
   *pstr++ = '\0';
 
-  pnum = strtoul(pstr, &end, 10);
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  s = getaddrinfo(hstr, pstr, &hints, &result);
 
-  if ((*pstr == '\0') || (*end != '\0') || (pnum == 0) || (pnum > 65535)) {
-    avrdude_message(MSG_INFO, "%s: net_open(): Bad port number \"%s\"\n",
-	    progname, pstr);
-    free(hstr);
-    return -1;
+  if (s != 0) {
+    avrdude_message(MSG_INFO,
+	    "%s: net_open(): Cannot resolve "
+	    "host=\"%s\", port=\"%s\": %s\n",
+	    progname, hstr, pstr, gai_strerror(s));
+    goto error;
   }
-
-  if ((hp = gethostbyname(hstr)) == NULL) {
-    avrdude_message(MSG_INFO, "%s: net_open(): unknown host \"%s\"\n",
-	    progname, hstr);
-    free(hstr);
-    return -1;
+  for (rp = result; rp != NULL; rp = rp->ai_next) {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if (fd == -1) {
+      /* This one failed, loop over */
+      continue;
+    }
+    if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
+      /* Success, we are connected */
+      break;
+    }
+    close(fd);
   }
-
-  free(hstr);
-
-  if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
-    avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n",
-	    progname, strerror(errno));
-    return -1;
+  if (rp == NULL) {
+    avrdude_message(MSG_INFO, "%s: net_open(): Cannot connect: %s\n",
+      progname, strerror(errno));
   }
-
-  memset(&sockaddr, 0, sizeof(struct sockaddr_in));
-  sockaddr.sin_family = AF_INET;
-  sockaddr.sin_port = htons(pnum);
-  memcpy(&(sockaddr.sin_addr.s_addr), hp->h_addr, sizeof(struct in_addr));
-
-  if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
-    avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n",
-	    progname, strerror(errno));
-    return -1;
+  else {
+    fdp->ifd = fd;
+    ret = 0;
   }
+  freeaddrinfo(result);
 
-  fdp->ifd = fd;
-  return 0;
+error:
+  free(hp);
+  return ret;
+#else
+  avrdude_message(MSG_INFO,
+    "%s: Networking is not supported on your platform.\n"
+    "If you need it, please open a bug report.\n", progname);
+  return -1;
+#endif /* HAVE_GETADDRINFO */
 }
 
 

From 4e22761f95e4490a90353600c4569eee71fceb13 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 23 Sep 2019 10:17:53 +0200
Subject: [PATCH 14/44] Fix of #2977

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 1d5d8f1a5..d4766ee72 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -810,8 +810,13 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
      */
 
     if (!item) {
-        if (wxOSX && col == nullptr)
-            UnselectAll();
+        if (col == nullptr) {
+            if (wxOSX)
+                UnselectAll();
+            else
+                return;
+        }
+
         if (evt_context_menu) {
             show_context_menu(evt_context_menu);
             return;
@@ -1330,7 +1335,7 @@ wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeTy
 
     for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
     {
-        if (type == ModelVolumeType::INVALID && item == "Slab")
+        if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0)
             continue;
         append_menu_item(sub_menu, wxID_ANY, _(item), "",
             [this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu);

From 4aec14ddabf35537cc53678e6cb21d6eb9410dd0 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 23 Sep 2019 11:11:43 +0200
Subject: [PATCH 15/44] =?UTF-8?q?Replace=20"mm3/s"=20with=20"mm=C2=B3/s"?=
 =?UTF-8?q?=20in=20preview=20legend=20for=20a=20"Volumetric=20flow=20rate"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libslic3r/GCode/PreviewData.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp
index 74de2f532..76f21daeb 100644
--- a/src/libslic3r/GCode/PreviewData.cpp
+++ b/src/libslic3r/GCode/PreviewData.cpp
@@ -359,7 +359,7 @@ std::string GCodePreviewData::get_legend_title() const
     case Extrusion::Feedrate:
         return L("Speed (mm/s)");
     case Extrusion::VolumetricRate:
-        return L("Volumetric flow rate (mm3/s)");
+        return L("Volumetric flow rate (mm³/s)");
     case Extrusion::Tool:
         return L("Tool");
     case Extrusion::ColorPrint:

From a6f5fe7bea91e4b89e9a462df1fec2d371b1a30d Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Mon, 23 Sep 2019 11:58:39 +0200
Subject: [PATCH 16/44] Fix arrange crash with incorrect geometries. Guard the
 case with tests.

---
 .../libnest2d/backends/clipper/geometries.hpp | 19 ++--
 .../include/libnest2d/geometry_traits.hpp     |  2 +-
 .../include/libnest2d/placers/nfpplacer.hpp   | 73 +-------------
 .../selections/selection_boilerplate.hpp      |  2 +-
 src/libnest2d/tests/test.cpp                  | 98 +++++++++++++------
 src/libslic3r/Arrange.cpp                     | 18 ++--
 6 files changed, 92 insertions(+), 120 deletions(-)

diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
index 56330e15e..57da6ec12 100644
--- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
+++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
@@ -81,17 +81,16 @@ inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance, const PolygonTag
     using ClipperLib::etClosedPolygon;
     using ClipperLib::Paths;
 
-    // If the input is not at least a triangle, we can not do this algorithm
-    if(sh.Contour.size() <= 3 ||
-       std::any_of(sh.Holes.begin(), sh.Holes.end(),
-                   [](const PathImpl& p) { return p.size() <= 3; })
-       ) throw GeometryException(GeomErr::OFFSET);
-
-    ClipperOffset offs;
     Paths result;
-    offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
-    offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
-    offs.Execute(result, static_cast<double>(distance));
+    
+    try {
+        ClipperOffset offs;
+        offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
+        offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
+        offs.Execute(result, static_cast<double>(distance));
+    } catch (ClipperLib::clipperException &) {
+        throw GeometryException(GeomErr::OFFSET);
+    }
 
     // Offsetting reverts the orientation and also removes the last vertex
     // so boost will not have a closed polygon.
diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp
index 827e2d8ba..72e239a70 100644
--- a/src/libnest2d/include/libnest2d/geometry_traits.hpp
+++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp
@@ -1144,7 +1144,7 @@ inline bool isInside(const TBGuest& ibb, const TBHost& box,
     auto minY = getY(box.minCorner());
     auto maxY = getY(box.maxCorner());
 
-    return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY;
+    return iminX >= minX && imaxX <= maxX && iminY >= minY && imaxY <= maxY;
 }
 
 template<class S, class TB>
diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
index 1a341d691..686857a87 100644
--- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
+++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
@@ -3,9 +3,6 @@
 
 #include <cassert>
 
-// For caching nfps
-#include <unordered_map>
-
 // For parallel for
 #include <functional>
 #include <iterator>
@@ -76,55 +73,6 @@ inline void enumerate(
 
 }
 
-namespace __itemhash {
-
-using Key = size_t;
-
-template<class S>
-Key hash(const _Item<S>& item) {
-    using Point = TPoint<S>;
-    using Segment = _Segment<Point>;
-
-    static const int N = 26;
-    static const int M = N*N - 1;
-
-    std::string ret;
-    auto& rhs = item.rawShape();
-    auto& ctr = sl::contour(rhs);
-    auto it = ctr.begin();
-    auto nx = std::next(it);
-
-    double circ = 0;
-    while(nx != ctr.end()) {
-        Segment seg(*it++, *nx++);
-        Radians a = seg.angleToXaxis();
-        double deg = Degrees(a);
-        int ms = 'A', ls = 'A';
-        while(deg > N) { ms++; deg -= N; }
-        ls += int(deg);
-        ret.push_back(char(ms)); ret.push_back(char(ls));
-        circ += std::sqrt(seg.template sqlength<double>());
-    }
-
-    it = ctr.begin(); nx = std::next(it);
-
-    while(nx != ctr.end()) {
-        Segment seg(*it++, *nx++);
-        auto l = int(M * std::sqrt(seg.template sqlength<double>()) / circ);
-        int ms = 'A', ls = 'A';
-        while(l > N) { ms++; l -= N; }
-        ls += l;
-        ret.push_back(char(ms)); ret.push_back(char(ls));
-    }
-
-    return std::hash<std::string>()(ret);
-}
-
-template<class S>
-using Hash = std::unordered_map<Key, nfp::NfpResult<S>>;
-
-}
-
 namespace placers {
 
 template<class RawShape>
@@ -529,17 +477,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
 
     using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
 
-    using ItemKeys = std::vector<__itemhash::Key>;
-
     // Norming factor for the optimization function
     const double norm_;
 
-    // Caching calculated nfps
-    __itemhash::Hash<RawShape> nfpcache_;
-
-    // Storing item hash keys
-    ItemKeys item_keys_;
-
 public:
 
     using Pile = nfp::Shapes<RawShape>;
@@ -636,15 +576,12 @@ public:
 private:
 
     using Shapes = TMultiShape<RawShape>;
-    using ItemRef = std::reference_wrapper<Item>;
-    using ItemWithHash = const std::pair<ItemRef, __itemhash::Key>;
 
-    Shapes calcnfp(const ItemWithHash itsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>)
+    Shapes calcnfp(const Item &trsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>)
     {
         using namespace nfp;
 
         Shapes nfps(items_.size());
-        const Item& trsh = itsh.first;
 
         // /////////////////////////////////////////////////////////////////////
         // TODO: this is a workaround and should be solved in Item with mutexes
@@ -678,12 +615,11 @@ private:
 
 
     template<class Level>
-    Shapes calcnfp( const ItemWithHash itsh, Level)
+    Shapes calcnfp(const Item &trsh, Level)
     { // Function for arbitrary level of nfp implementation
         using namespace nfp;
 
         Shapes nfps;
-        const Item& trsh = itsh.first;
 
         auto& orb = trsh.transformedShape();
         bool orbconvex = trsh.isContourConvex();
@@ -849,8 +785,6 @@ private:
             remlist.insert(remlist.end(), remaining.from, remaining.to);
         }
 
-        size_t itemhash = __itemhash::hash(item);
-
         if(items_.empty()) {
             setInitialPosition(item);
             best_overfit = overfit(item.transformedShape(), bin_);
@@ -875,7 +809,7 @@ private:
                 // it is disjunct from the current merged pile
                 placeOutsideOfBin(item);
 
-                nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
+                nfps = calcnfp(item, Lvl<MaxNfpLevel::value>());
 
                 auto iv = item.referenceVertex();
 
@@ -1112,7 +1046,6 @@ private:
 
         if(can_pack) {
             ret = PackResult(item);
-            item_keys_.emplace_back(itemhash);
         } else {
             ret = PackResult(best_overfit);
         }
diff --git a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
index 2df9a26c3..36fec7164 100644
--- a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
+++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
@@ -43,7 +43,7 @@ protected:
             
             Placer p{bin};
             p.configure(pcfg);
-            if (!p.pack(cpy)) it = c.erase(it);
+            if (itm.area() <= 0 || !p.pack(cpy)) it = c.erase(it);
             else it++;
         }
     }
diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp
index 4a6691415..5ba28228a 100644
--- a/src/libnest2d/tests/test.cpp
+++ b/src/libnest2d/tests/test.cpp
@@ -40,7 +40,7 @@ struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
 }
 }
 
-std::vector<libnest2d::Item>& prusaParts() {
+static std::vector<libnest2d::Item>& prusaParts() {
     static std::vector<libnest2d::Item> ret;
     
     if(ret.empty()) {
@@ -51,7 +51,7 @@ std::vector<libnest2d::Item>& prusaParts() {
     return ret;
 }
 
-TEST(BasicFunctionality, Angles)
+TEST(GeometryAlgorithms, Angles)
 {
     
     using namespace libnest2d;
@@ -109,7 +109,7 @@ TEST(BasicFunctionality, Angles)
 }
 
 // Simple test, does not use gmock
-TEST(BasicFunctionality, creationAndDestruction)
+TEST(Nesting, ItemCreationAndDestruction)
 {
     using namespace libnest2d;
     
@@ -572,26 +572,74 @@ TEST(GeometryAlgorithms, convexHull) {
 }
 
 
-TEST(GeometryAlgorithms, NestTest) {
+TEST(Nesting, NestPrusaPartsShouldFitIntoTwoBins) {
+    
+    // Get the input items and define the bin.
     std::vector<Item> input = prusaParts();
-
-    libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) {
-        std::cout << "parts left: " << cnt << std::endl;
+    auto bin = Box(250000000, 210000000);
+    
+    // Do the nesting. Check in each step if the remaining items are less than
+    // in the previous step. (Some algorithms can place more items in one step)
+    size_t pcount = input.size();
+    libnest2d::nest(input, bin, [&pcount](unsigned cnt) {
+        ASSERT_TRUE(cnt < pcount);
+        pcount = cnt;
     });
-
+    
+    // Get the number of logical bins: search for the max binId...
     auto max_binid_it = std::max_element(input.begin(), input.end(),
                                          [](const Item &i1, const Item &i2) {
                                              return i1.binId() < i2.binId();
                                          });
-
-    size_t bins = max_binid_it == input.end() ? 0 : max_binid_it->binId() + 1;
     
-    ASSERT_EQ(bins, 2u);
-
+    auto bins = size_t(max_binid_it == input.end() ? 0 :
+                                                     max_binid_it->binId() + 1);
+    
+    // For prusa parts, 2 bins should be enough...
+    ASSERT_LE(bins, 2u);
+    
+    // All parts should be processed by the algorithm
     ASSERT_TRUE(
         std::all_of(input.begin(), input.end(), [](const Item &itm) {
             return itm.binId() != BIN_ID_UNSET;
         }));
+    
+    // Gather the items into piles of arranged polygons...
+    using Pile = TMultiShape<ClipperLib::Polygon>;
+    std::vector<Pile> piles(bins);
+    
+    for (auto &itm : input)
+        piles[size_t(itm.binId())].emplace_back(itm.transformedShape());
+    
+    // Now check all the piles, the bounding box of each pile should be inside
+    // the defined bin.
+    for (auto &pile : piles) {
+        auto bb = sl::boundingBox(pile);
+        ASSERT_TRUE(sl::isInside(bb, bin));
+    }
+}
+
+TEST(Nesting, NestEmptyItemShouldBeUntouched) {
+    auto bin = Box(250000000, 210000000); // dummy bin
+    
+    std::vector<Item> items;
+    items.emplace_back(Item{});   // Emplace empty item
+    items.emplace_back(Item{0, 200, 0});   // Emplace zero area item
+    
+    libnest2d::nest(items, bin);
+    
+    for (auto &itm : items) ASSERT_EQ(itm.binId(), BIN_ID_UNSET);
+}
+
+TEST(Nesting, NestLargeItemShouldBeUntouched) {
+    auto bin = Box(250000000, 210000000); // dummy bin
+    
+    std::vector<Item> items;
+    items.emplace_back(Rectangle{250000001, 210000001});  // Emplace large item
+    
+    libnest2d::nest(items, bin);
+    
+    ASSERT_EQ(items.front().binId(), BIN_ID_UNSET);
 }
 
 namespace {
@@ -966,26 +1014,20 @@ using Ratio = boost::rational<boost::multiprecision::int128_t>;
 
 }
 
-TEST(RotatingCalipers, MinAreaBBCClk) {
-    auto u = [](ClipperLib::cInt n) { return n*1000000; };
-    PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
+//TEST(GeometryAlgorithms, MinAreaBBCClk) {
+//    auto u = [](ClipperLib::cInt n) { return n*1000000; };
+//    PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
     
-    long double arearef = refMinAreaBox(poly);
-    long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
+//    long double arearef = refMinAreaBox(poly);
+//    long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
     
-    ASSERT_LE(std::abs(area - arearef), 500e6 );
-}
+//    ASSERT_LE(std::abs(area - arearef), 500e6 );
+//}
 
-TEST(RotatingCalipers, AllPrusaMinBB) {
-    //    /size_t idx = 0;
+TEST(GeometryAlgorithms, MinAreaBBWithRotatingCalipers) {
     long double err_epsilon = 500e6l;
     
     for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) {
-        //        ClipperLib::Path rinput = PRINTER_PART_POLYGONS[idx];
-        //        rinput.pop_back();
-        //        std::reverse(rinput.begin(), rinput.end());
-        
-        //        PolygonImpl poly(removeCollinearPoints<PathImpl, PointImpl, Unit>(rinput, 1000000));
         PolygonImpl poly(rinput);
         
         long double arearef = refMinAreaBox(poly);
@@ -993,8 +1035,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) {
         long double area = cast<long double>(bb.area());
         
         bool succ = std::abs(arearef - area) < err_epsilon;
-        //        std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: " 
-//                  << arearef << " actual: " << area << std::endl;
         
         ASSERT_TRUE(succ);
     }
@@ -1011,8 +1051,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) {
         
         
         bool succ = std::abs(arearef - area) < err_epsilon;
-        //        std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: " 
-//                  << arearef << " actual: " << area << std::endl;
         
         ASSERT_TRUE(succ);
     }
diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp
index 52168c929..20dfd8926 100644
--- a/src/libslic3r/Arrange.cpp
+++ b/src/libslic3r/Arrange.cpp
@@ -618,19 +618,21 @@ void arrange(ArrangePolygons &             arrangables,
     items.reserve(arrangables.size());
     
     // Create Item from Arrangeable
-    auto process_arrangeable =
-        [](const ArrangePolygon &arrpoly, std::vector<Item> &outp)
+    auto process_arrangeable = [](const ArrangePolygon &arrpoly,
+                                  std::vector<Item> &   outp)
     {
-        Polygon p        = arrpoly.poly.contour;
-        const Vec2crd &  offs     = arrpoly.translation;
-        double           rotation = arrpoly.rotation;
+        Polygon        p        = arrpoly.poly.contour;
+        const Vec2crd &offs     = arrpoly.translation;
+        double         rotation = arrpoly.rotation;
 
         if (p.is_counter_clockwise()) p.reverse();
 
         clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
-
-        auto firstp = clpath.Contour.front();
-        clpath.Contour.emplace_back(firstp);
+        
+        if (!clpath.Contour.empty()) {
+            auto firstp = clpath.Contour.front();
+            clpath.Contour.emplace_back(firstp);
+        }
 
         outp.emplace_back(std::move(clpath));
         outp.back().rotation(rotation);

From c0c937425f17a03a484ac6af28a2a515dbf4d17b Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 23 Sep 2019 14:24:53 +0200
Subject: [PATCH 17/44] Added missed comment to
 https://github.com/prusa3d/PrusaSlicer/commit/7e060f84bd534fb027bdc3a1f4b48cce912090bd

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index d4766ee72..573fbe980 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -273,6 +273,8 @@ void ObjectList::create_objects_ctrl()
     AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em,
         wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
 
+    // For some reason under OSX on 4K(5K) monitors in wxDataViewColumn constructor doesn't set width of column.
+    // Therefore, force set column width.
     if (wxOSX)
     {
         GetColumn(colName)->SetWidth(20*em);
@@ -3682,10 +3684,10 @@ void ObjectList::msw_rescale()
     // update min size !!! A width of control shouldn't be a wxDefaultCoord
     SetMinSize(wxSize(1, 15 * em));
 
-    GetColumn(colName)->SetWidth(19 * em);
-    GetColumn(colPrint)->SetWidth( 2 * em);
+    GetColumn(colName    )->SetWidth(20 * em);
+    GetColumn(colPrint   )->SetWidth( 3 * em);
     GetColumn(colExtruder)->SetWidth( 8 * em);
-    GetColumn(colEditing)->SetWidth( 2 * em);
+    GetColumn(colEditing )->SetWidth( 3 * em);
 
     // rescale all icons, used by ObjectList
     msw_rescale_icons();

From 82a52e8b2168740327d025d5f326077472e6fe00 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 23 Sep 2019 14:56:27 +0200
Subject: [PATCH 18/44] #2922 - Port of commit:
 https://github.com/prusa3d/PrusaSlicer/pull/2922/commits/f42edd35ce9e438de2c5fcacb69bfa66d6a28bbc
 by jschuh

---
 src/libslic3r/ExtrusionEntity.hpp   | 18 +++++----
 src/libslic3r/GCode/Analyzer.cpp    | 59 ++++++++++++++++++++++++++---
 src/libslic3r/GCode/Analyzer.hpp    | 14 ++++++-
 src/libslic3r/GCode/PreviewData.cpp | 13 +++++++
 src/libslic3r/GCode/PreviewData.hpp |  4 ++
 src/slic3r/GUI/GLCanvas3D.cpp       |  4 ++
 src/slic3r/GUI/GUI_Preview.cpp      |  1 +
 7 files changed, 98 insertions(+), 15 deletions(-)

diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index ce52ae152..3c6bde47a 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -121,17 +121,19 @@ public:
     unsigned int extruder_id;
     // Id of the color, used for visualization purposes in the color printing case.
     unsigned int cp_color_id;
+    // Fan speed for the extrusion, used for visualization purposes.
+    float fan_speed;
 
-    ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}
-    ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}
-    ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
-	ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
-	ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
-	ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
+    ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), fan_speed(0.0f), m_role(role) {};
+    ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), fan_speed(0.0f), m_role(role) {};
+    ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {}
+    ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {}
+    ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {}
+    ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {}
 //    ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {};
 
-    ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; }
-    ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; }
+    ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->fan_speed = rhs.fan_speed, this->polyline = rhs.polyline; return *this; }
+    ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->fan_speed = rhs.fan_speed, this->polyline = std::move(rhs.polyline); return *this; }
 
 	ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); }
     // Create a new object, initialize it with this object using the move semantics.
diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp
index 20f0483b0..fa4414da9 100644
--- a/src/libslic3r/GCode/Analyzer.cpp
+++ b/src/libslic3r/GCode/Analyzer.cpp
@@ -20,6 +20,7 @@ static const unsigned int DEFAULT_EXTRUDER_ID = 0;
 static const unsigned int DEFAULT_COLOR_PRINT_ID = 0;
 static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f);
 static const float DEFAULT_START_EXTRUSION = 0.0f;
+static const float DEFAULT_FAN_SPEED = 0.0f;
 
 namespace Slic3r {
 
@@ -36,21 +37,23 @@ const float GCodeAnalyzer::Default_Height = 0.0f;
 GCodeAnalyzer::Metadata::Metadata()
     : extrusion_role(erNone)
     , extruder_id(DEFAULT_EXTRUDER_ID)
-    , cp_color_id(DEFAULT_COLOR_PRINT_ID)
     , mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm)
     , width(GCodeAnalyzer::Default_Width)
     , height(GCodeAnalyzer::Default_Height)
     , feedrate(DEFAULT_FEEDRATE)
+    , fan_speed(DEFAULT_FAN_SPEED)
+    , cp_color_id(DEFAULT_COLOR_PRINT_ID)
 {
 }
 
-GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id/* = 0*/)
+GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id/* = 0*/)
     : extrusion_role(extrusion_role)
     , extruder_id(extruder_id)
     , mm3_per_mm(mm3_per_mm)
     , width(width)
     , height(height)
     , feedrate(feedrate)
+    , fan_speed(fan_speed)
     , cp_color_id(cp_color_id)
 {
 }
@@ -75,15 +78,18 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other)
     if (feedrate != other.feedrate)
         return true;
 
+    if (fan_speed != other.fan_speed)
+        return true;
+
     if (cp_color_id != other.cp_color_id)
         return true;
 
     return false;
 }
 
-GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id/* = 0*/)
+GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id/* = 0*/)
     : type(type)
-    , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, cp_color_id)
+    , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, fan_speed, cp_color_id)
     , start_position(start_position)
     , end_position(end_position)
     , delta_extruder(delta_extruder)
@@ -133,6 +139,7 @@ void GCodeAnalyzer::reset()
     _set_feedrate(DEFAULT_FEEDRATE);
     _set_start_position(DEFAULT_START_POSITION);
     _set_start_extrusion(DEFAULT_START_EXTRUSION);
+    _set_fan_speed(DEFAULT_FAN_SPEED);
     _reset_axes_position();
     _reset_cached_position();
 
@@ -259,6 +266,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
                         _processM83(line);
                         break;
                     }
+                case 106: // Set fan speed
+                    {
+                        _processM106(line);
+                        break;
+                    }
+                case 107: // Disable fan
+                    {
+                        _processM107(line);
+                        break;
+                    }
                 case 108:
                 case 135:
                     {
@@ -448,6 +465,24 @@ void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line)
     _set_e_local_positioning_type(Relative);
 }
 
+void GCodeAnalyzer::_processM106(const GCodeReader::GCodeLine& line)
+{
+    if (!line.has('P'))
+    {
+        // The absence of P means the print cooling fan, so ignore anything else.
+        float new_fan_speed;
+        if (line.has_value('S', new_fan_speed))
+            _set_fan_speed((100.0f / 256.0f) * new_fan_speed);
+        else
+            _set_fan_speed(100.0f);
+    }
+}
+
+void GCodeAnalyzer::_processM107(const GCodeReader::GCodeLine& line)
+{
+    _set_fan_speed(0.0f);
+}
+
 void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
 {
     // These M-codes are used by MakerWare and Sailfish to change active tool.
@@ -726,6 +761,16 @@ float GCodeAnalyzer::_get_feedrate() const
     return m_state.data.feedrate;
 }
 
+void GCodeAnalyzer::_set_fan_speed(float fan_speed_percentage)
+{
+    m_state.data.fan_speed = fan_speed_percentage;
+}
+
+float GCodeAnalyzer::_get_fan_speed() const
+{
+    return m_state.data.fan_speed;
+}
+
 void GCodeAnalyzer::_set_axis_position(EAxis axis, float position)
 {
     m_state.position[axis] = position;
@@ -798,7 +843,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type)
 
     Vec3d start_position = _get_start_position() + extruder_offset;
     Vec3d end_position = _get_end_position() + extruder_offset;
-    it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id());
+    it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id());
 }
 
 bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const
@@ -834,6 +879,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
                 path.polyline = polyline;
                 path.feedrate = data.feedrate;
                 path.extruder_id = data.extruder_id;
+                path.fan_speed = data.fan_speed;
                 path.cp_color_id = data.cp_color_id;
 
                 get_layer_at_z(preview_data.extrusion.layers, z).paths.push_back(path);
@@ -854,6 +900,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
     GCodePreviewData::Range width_range;
     GCodePreviewData::Range feedrate_range;
     GCodePreviewData::Range volumetric_rate_range;
+    GCodePreviewData::Range fan_speed_range;
 
     // to avoid to call the callback too often
     unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1);
@@ -888,6 +935,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
             width_range.update_from(move.data.width);
             feedrate_range.update_from(move.data.feedrate);
             volumetric_rate_range.update_from(volumetric_rate);
+            fan_speed_range.update_from(move.data.fan_speed);
         }
         else
             // append end vertex of the move to current polyline
@@ -906,6 +954,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
     preview_data.ranges.width.update_from(width_range);
     preview_data.ranges.feedrate.update_from(feedrate_range);
     preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
+    preview_data.ranges.fan_speed.update_from(fan_speed_range);
 
     // we need to sort the layers by their z as they can be shuffled in case of sequential prints
     std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; });
diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp
index 0372d9da7..529610b0b 100644
--- a/src/libslic3r/GCode/Analyzer.hpp
+++ b/src/libslic3r/GCode/Analyzer.hpp
@@ -54,10 +54,11 @@ public:
         float width;     // mm
         float height;    // mm
         float feedrate;  // mm/s
+        float fan_speed; // percentage
         unsigned int cp_color_id;
 
         Metadata();
-        Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id = 0);
+        Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id = 0);
 
         bool operator != (const Metadata& other) const;
     };
@@ -81,7 +82,7 @@ public:
         Vec3d end_position;
         float delta_extruder;
 
-        GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id = 0);
+        GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id = 0);
         GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder);
     };
 
@@ -171,6 +172,12 @@ private:
     // Set extruder to relative mode
     void _processM83(const GCodeReader::GCodeLine& line);
 
+    // Set fan speed
+    void _processM106(const GCodeReader::GCodeLine& line);
+
+    // Disable fan
+    void _processM107(const GCodeReader::GCodeLine& line);
+
     // Set tool (MakerWare and Sailfish flavor)
     void _processM108orM135(const GCodeReader::GCodeLine& line);
 
@@ -233,6 +240,9 @@ private:
     void _set_feedrate(float feedrate_mm_sec);
     float _get_feedrate() const;
 
+    void _set_fan_speed(float fan_speed_percentage);
+    float _get_fan_speed() const;
+
     void _set_axis_position(EAxis axis, float position);
     float _get_axis_position(EAxis axis) const;
 
diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp
index 76f21daeb..c6cfcc8af 100644
--- a/src/libslic3r/GCode/PreviewData.cpp
+++ b/src/libslic3r/GCode/PreviewData.cpp
@@ -241,6 +241,7 @@ void GCodePreviewData::set_default()
     ::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));
+    ::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
     ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
 
     extrusion.set_default();
@@ -287,6 +288,11 @@ GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) con
     return ranges.feedrate.get_color_at(feedrate);
 }
 
+GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const
+{
+    return ranges.fan_speed.get_color_at(fan_speed);
+}
+
 GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
 {
     return ranges.volumetric_rate.get_color_at(rate);
@@ -358,6 +364,8 @@ std::string GCodePreviewData::get_legend_title() const
         return L("Width (mm)");
     case Extrusion::Feedrate:
         return L("Speed (mm/s)");
+    case Extrusion::FanSpeed:
+        return L("Fan Speed (%)");
     case Extrusion::VolumetricRate:
         return L("Volumetric flow rate (mm³/s)");
     case Extrusion::Tool:
@@ -421,6 +429,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
             Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f);
             break;
         }
+    case Extrusion::FanSpeed:
+        {
+            Helper::FillListFromRange(items, ranges.fan_speed, 0, 1.0f);
+            break;
+        }
     case Extrusion::VolumetricRate:
         {
             Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f);
diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp
index 6490399b4..7f5b691e1 100644
--- a/src/libslic3r/GCode/PreviewData.hpp
+++ b/src/libslic3r/GCode/PreviewData.hpp
@@ -52,6 +52,8 @@ public:
         Range width;
         // Color mapping by feedrate.
         Range feedrate;
+        // Color mapping by fan speed.
+        Range fan_speed;
         // Color mapping by volumetric extrusion rate.
         Range volumetric_rate;
     };
@@ -74,6 +76,7 @@ public:
             Height,
             Width,
             Feedrate,
+            FanSpeed,
             VolumetricRate,
             Tool,
             ColorPrint,
@@ -205,6 +208,7 @@ public:
     Color get_height_color(float height) const;
     Color get_width_color(float width) const;
     Color get_feedrate_color(float feedrate) const;
+    Color get_fan_speed_color(float fan_speed) const;
     Color get_volumetric_rate_color(float rate) const;
 
     void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 715d9f806..7656691ce 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -5001,6 +5001,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
                 return path.width;
             case GCodePreviewData::Extrusion::Feedrate:
                 return path.feedrate;
+            case GCodePreviewData::Extrusion::FanSpeed:
+                return path.fan_speed;
             case GCodePreviewData::Extrusion::VolumetricRate:
                 return path.feedrate * (float)path.mm3_per_mm;
             case GCodePreviewData::Extrusion::Tool:
@@ -5026,6 +5028,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
                 return data.get_width_color(value);
             case GCodePreviewData::Extrusion::Feedrate:
                 return data.get_feedrate_color(value);
+            case GCodePreviewData::Extrusion::FanSpeed:
+                return data.get_fan_speed_color(value);
             case GCodePreviewData::Extrusion::VolumetricRate:
                 return data.get_volumetric_rate_color(value);
             case GCodePreviewData::Extrusion::Tool:
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 7c761ed5f..b6350bcab 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
     m_choice_view_type->Append(_(L("Height")));
     m_choice_view_type->Append(_(L("Width")));
     m_choice_view_type->Append(_(L("Speed")));
+    m_choice_view_type->Append(_(L("Fan speed")));
     m_choice_view_type->Append(_(L("Volumetric flow rate")));
     m_choice_view_type->Append(_(L("Tool")));
     m_choice_view_type->Append(_(L("Color Print")));

From a234193dea9f46f65c497cee40acf2eabcdb51e4 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 23 Sep 2019 16:29:07 +0200
Subject: [PATCH 19/44] Fixed localization for "Configuration Wizard" menu item

---
 src/slic3r/GUI/ConfigWizard.cpp | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index 0115ff5f9..4e7aff028 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -1225,13 +1225,16 @@ bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater
 
 const wxString& ConfigWizard::name(const bool from_menu/* = false*/)
 {
-    // A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
+    // A different naming convention is used for the Wizard on Windows & GTK vs. OSX.
+    // Note: Don't call _() macro here.
+    //       This function just return the current name according to the OS.
+    //       Translation is implemented inside GUI_App::add_config_menu()
 #if __APPLE__
-    static const wxString config_wizard_name =  _(L("Configuration Assistant"));
-    static const wxString config_wizard_name_menu = _(L("Configuration &Assistant"));
+    static const wxString config_wizard_name =  L("Configuration Assistant");
+    static const wxString config_wizard_name_menu = L("Configuration &Assistant");
 #else
-    static const wxString config_wizard_name = _(L("Configuration Wizard"));
-    static const wxString config_wizard_name_menu = _(L("Configuration &Wizard"));
+    static const wxString config_wizard_name = L("Configuration Wizard");
+    static const wxString config_wizard_name_menu = L("Configuration &Wizard");
 #endif
     return from_menu ? config_wizard_name_menu : config_wizard_name;
 }

From 197a1f95ffbd1986b36b13c0593d9188b8d1dc43 Mon Sep 17 00:00:00 2001
From: Jason Tibbitts <j@tib.bs>
Date: Mon, 23 Sep 2019 12:10:02 -0500
Subject: [PATCH 20/44] Relocate declaration of stl_internal_reverse_quads

On little-endian hosts, stl_internal_reverse_quads will be called before
it is declared.  Move the declaration up to the beginning of the file to
fix build breakage seen on an s390x host.
---
 src/admesh/stlinit.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp
index 693aad086..390fe56a4 100644
--- a/src/admesh/stlinit.cpp
+++ b/src/admesh/stlinit.cpp
@@ -36,6 +36,10 @@
 #error "SEEK_SET not defined"
 #endif
 
+#ifndef BOOST_LITTLE_ENDIAN
+extern void stl_internal_reverse_quads(char *buf, size_t cnt);
+#endif /* BOOST_LITTLE_ENDIAN */
+
 static FILE* stl_open_count_facets(stl_file *stl, const char *file) 
 {
   	// Open the file in binary mode first.
@@ -238,10 +242,6 @@ bool stl_open(stl_file *stl, const char *file)
   	return result;
 }
 
-#ifndef BOOST_LITTLE_ENDIAN
-extern void stl_internal_reverse_quads(char *buf, size_t cnt);
-#endif /* BOOST_LITTLE_ENDIAN */
-
 void stl_allocate(stl_file *stl) 
 {
   	//  Allocate memory for the entire .STL file.

From e6604afd05da4df1ce355b92bf33ea20797a5742 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 24 Sep 2019 09:11:27 +0200
Subject: [PATCH 21/44] #2972 - Fixed selection after adding an instance with
 autocenter active

---
 src/slic3r/GUI/Plater.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 28474cac9..c7f1f4c5c 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -4288,11 +4288,10 @@ void Plater::increase_instances(size_t num)
 
     sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
 
-    if (p->get_config("autocenter") == "1") {
+    if (p->get_config("autocenter") == "1")
         p->arrange();
-    } else {
-        p->update();
-    }
+
+    p->update();
 
     p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1);
 

From c1e3be9b27c3ac641ff6dd00692516cb164bd76d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 24 Sep 2019 12:48:05 +0200
Subject: [PATCH 22/44] MeshUtils.cpp: MeshRaycaster is now aware of the
 clipping plane

---
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 48 +++++++-------------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp |  2 +-
 src/slic3r/GUI/MeshUtils.cpp                 | 41 +++++++++++------
 src/slic3r/GUI/MeshUtils.hpp                 |  5 +-
 4 files changed, 46 insertions(+), 50 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 992f4912a..000ddf8c9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -252,7 +252,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
         const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
         const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
 
-        if (is_point_clipped(support_point.pos.cast<double>()))
+        if (is_mesh_point_clipped(support_point.pos.cast<double>()))
             continue;
 
         // First decide about the color of the point.
@@ -335,14 +335,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
 
 
 
-bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
+bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
 {
     if (m_clipping_plane_distance == 0.f)
         return false;
 
     Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
     transformed_point(2) += m_z_shift;
-    return m_clipping_plane->distance(transformed_point) < 0.;
+    return m_clipping_plane->is_point_clipped(transformed_point);
 }
 
 
@@ -391,27 +391,15 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
     trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
 
     // The raycaster query
-    std::vector<Vec3f> hits;
-    std::vector<Vec3f> normals;
-    m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals);
-
-    // We must also take care of the clipping plane (if active)
-    unsigned i = 0;
-    if (m_clipping_plane_distance != 0.f) {
-        for (i=0; i<hits.size(); ++i)
-            if (! is_point_clipped(hits[i].cast<double>()))
-                break;
+    Vec3f hit;
+    Vec3f normal;
+    if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) {
+        // Return both the point and the facet normal.
+        pos_and_normal = std::make_pair(hit, normal);
+        return true;
     }
-
-    if (i==hits.size() || (hits.size()-i) % 2 != 0) {
-        // All hits are either clipped, or there is an odd number of unclipped
-        // hits - meaning the nearest must be from inside the mesh.
+    else
         return false;
-    }
-
-    // Calculate and return both the point and the facet normal.
-    pos_and_normal = std::make_pair(hits[i], normals[i]);
-    return true;
 }
 
 // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
@@ -481,19 +469,15 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
             std::vector<Vec3f> points_inside;
             std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
             for (size_t idx : points_idxs)
-                points_inside.push_back((trafo.get_matrix() * points[idx]).cast<float>());
+                points_inside.push_back(points[idx].cast<float>());
 
             // Only select/deselect points that are actually visible
-            for (size_t idx :  m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside,
-                          [this](const Vec3f& pt) { return is_point_clipped(pt.cast<double>()); }))
+            for (size_t idx :  m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get()))
             {
-                const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point;
-                if (! is_point_clipped(support_point.pos.cast<double>())) {
-                    if (rectangle_status == GLSelectionRectangle::Deselect)
-                        unselect_point(points_idxs[idx]);
-                    else
-                        select_point(points_idxs[idx]);
-                }
+                if (rectangle_status == GLSelectionRectangle::Deselect)
+                    unselect_point(points_idxs[idx]);
+                else
+                    select_point(points_idxs[idx]);
             }
             return true;
         }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index cf5245f19..15b2df80e 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -125,7 +125,7 @@ private:
     mutable std::unique_ptr<MeshClipper> m_supports_clipper;
 
     std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
-    bool is_point_clipped(const Vec3d& point) const;
+    bool is_mesh_point_clipped(const Vec3d& point) const;
     //void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
 
     // Methods that do the model_object and editing cache synchronization,
diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp
index e559020e9..62a6813a6 100644
--- a/src/slic3r/GUI/MeshUtils.cpp
+++ b/src/slic3r/GUI/MeshUtils.cpp
@@ -152,8 +152,8 @@ Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const
 }
 
 
-bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo,
-                                      const Camera& camera, std::vector<Vec3f>* positions, std::vector<Vec3f>* normals) const
+bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
+                                      Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const
 {
     const std::array<int, 4>& viewport = camera.get_viewport();
     const Transform3d& model_mat = camera.get_view_matrix();
@@ -179,25 +179,30 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
 
     std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
 
-    // Now stuff the points in the provided vector and calculate normals if asked about them:
-    if (positions != nullptr) {
-        positions->clear();
-        if (normals != nullptr)
-            normals->clear();
-        for (const igl::Hit& hit : hits) {
-            positions->push_back(m_AABB_wrapper->get_hit_pos(hit));
+    unsigned i = 0;
 
-            if (normals != nullptr)
-                normals->push_back(m_AABB_wrapper->get_hit_normal(hit));
+    // Remove points that are obscured or cut by the clipping plane
+    if (clipping_plane) {
+        for (i=0; i<hits.size(); ++i)
+            if (! clipping_plane->is_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast<double>()))
+                break;
+
+        if (i==hits.size() || (hits.size()-i) % 2 != 0) {
+            // All hits are either clipped, or there is an odd number of unclipped
+            // hits - meaning the nearest must be from inside the mesh.
+            return false;
         }
     }
 
+    // Now stuff the points in the provided vector and calculate normals if asked about them:
+    position = m_AABB_wrapper->get_hit_pos(hits[i]);
+    normal = m_AABB_wrapper->get_hit_normal(hits[i]);
     return true;
 }
 
 
 std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
-                                                       std::function<bool(const Vec3f&)> fn_ignore_hit) const
+                                                       const ClippingPlane* clipping_plane) const
 {
     std::vector<unsigned> out;
 
@@ -206,19 +211,24 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
     Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
     Vec3f scaling = trafo.get_scaling_factor().cast<float>();
     direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
+    const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
 
     for (size_t i=0; i<points.size(); ++i) {
         const Vec3f& pt = points[i];
+        if (clipping_plane && clipping_plane->is_point_clipped(pt.cast<double>()))
+            continue;
+
         bool is_obscured = false;
         // Cast a ray in the direction of the camera and look for intersection with the mesh:
         std::vector<igl::Hit> hits;
-        // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
+        // Offset the start of the ray by EPSILON to account for numerical inaccuracies.
         if (m_AABB_wrapper->m_AABB.intersect_ray(
                 AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3),
                 AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3),
-                pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
+                inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
 
             std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
+
             // If the closest hit facet normal points in the same direction as the ray,
             // we are looking through the mesh and should therefore discard the point:
             if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f)
@@ -227,11 +237,12 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
             // Eradicate all hits that the caller wants to ignore
             for (unsigned j=0; j<hits.size(); ++j) {
                 const igl::Hit& hit = hits[j];
-                if (fn_ignore_hit(m_AABB_wrapper->get_hit_pos(hit))) {
+                if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast<double>())) {
                     hits.erase(hits.begin()+j);
                     --j;
                 }
             }
+
             // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
             // Also, the threshold is in mesh coordinates, not in actual dimensions.
             if (! hits.empty())
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
index a2be2677b..e4c4c20d2 100644
--- a/src/slic3r/GUI/MeshUtils.hpp
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -50,6 +50,7 @@ public:
         return (-get_normal().dot(pt) + m_data[3]);
     }
 
+    bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; }
     void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
     void set_offset(double offset) { m_data[3] = offset; }
     Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
@@ -98,10 +99,10 @@ public:
     void set_camera(const Camera& camera);
 
     bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
-                           std::vector<Vec3f>* positions = nullptr, std::vector<Vec3f>* normals = nullptr) const;
+                           Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const;
 
     std::vector<unsigned> get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera,
-                                              const std::vector<Vec3f>& points, std::function<bool(const Vec3f&)> fn_ignore_hit) const;
+                                              const std::vector<Vec3f>& points, const ClippingPlane* clipping_plane = nullptr) const;
 
     Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
 

From 7c1706f7eee78bad6492c114289e85fa6a0c5293 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 24 Sep 2019 14:13:03 +0200
Subject: [PATCH 23/44] Follow-up of f4910cafed7d1cc28c7db0eb952dced90c66a9de
 -> Fix extended to Mac

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 573fbe980..37d45ea3a 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -753,9 +753,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
     }
 
     select_items(items);
-#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
     selection_changed();
-#endif //no __WXOSX__ //__WXMSW__
+//#endif //no __WXOSX__ //__WXMSW__
 }
 
 void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
@@ -773,9 +773,9 @@ void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
     wxGetApp().plater()->changed_objects(object_idxs);
 
     select_items(items);
-#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
     selection_changed();
-#endif //no __WXOSX__ //__WXMSW__
+//#endif //no __WXOSX__ //__WXMSW__
 }
 
 #ifdef __WXOSX__
@@ -1720,9 +1720,9 @@ void ObjectList::load_subobject(ModelVolumeType type)
     if (sel_item)
         select_item(sel_item);
 
-#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
     selection_changed();
-#endif //no __WXOSX__ //__WXMSW__
+//#endif //no __WXOSX__ //__WXMSW__
 }
 
 void ObjectList::load_part( ModelObject* model_object,
@@ -1858,9 +1858,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
     const auto object_item = m_objects_model->GetTopParent(GetSelection());
     select_item(m_objects_model->AddVolumeChild(object_item, name, type, 
         new_volume->get_mesh_errors_count()>0));
-#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
     selection_changed();
-#endif //no __WXOSX__ //__WXMSW__
+//#endif //no __WXOSX__ //__WXMSW__
 }
 
 void ObjectList::load_shape_object(const std::string& type_name)

From 9607fbeb6db57b3433224a34829818ffe0db5bea Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 24 Sep 2019 14:32:59 +0200
Subject: [PATCH 24/44] Fix of SPE-1027

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 573fbe980..b2da8e2c5 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -815,7 +815,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
         if (col == nullptr) {
             if (wxOSX)
                 UnselectAll();
-            else
+            else if (!evt_context_menu) 
+                // Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED,
+                // which invoked next list_manipulation(false)
                 return;
         }
 

From 7861fa50866d2a73e22b8d7ae8d05e07f46257e4 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 24 Sep 2019 16:01:01 +0200
Subject: [PATCH 25/44] Whitespace changes to supress misleading indentation
 warnings These appear in newer gcc when spaces and tabs are mixed

---
 src/clipper/clipper_z.hpp             |  2 +-
 src/libslic3r/Config.cpp              |  2 +-
 src/libslic3r/Config.hpp              |  6 +++---
 src/libslic3r/Format/AMF.cpp          |  2 +-
 src/libslic3r/GCode.cpp               |  4 ++--
 src/libslic3r/GCode/ToolOrdering.cpp  |  2 +-
 src/libslic3r/GCode/WipeTower.cpp     | 10 +++++-----
 src/libslic3r/LayerRegion.cpp         |  4 ++--
 src/libslic3r/Print.cpp               |  2 +-
 src/libslic3r/PrintObject.cpp         |  2 +-
 src/libslic3r/SupportMaterial.cpp     |  6 +++---
 src/slic3r/Config/Snapshot.cpp        |  8 ++++----
 src/slic3r/Config/Version.cpp         | 10 +++++-----
 src/slic3r/GUI/BedShapeDialog.cpp     |  2 +-
 src/slic3r/GUI/GLCanvas3D.cpp         | 12 ++++++------
 src/slic3r/GUI/GUI.cpp                |  2 +-
 src/slic3r/GUI/GUI_App.cpp            |  2 +-
 src/slic3r/GUI/LambdaObjectDialog.cpp |  2 +-
 src/slic3r/GUI/MainFrame.cpp          |  2 +-
 src/slic3r/GUI/OptionsGroup.cpp       |  2 +-
 src/slic3r/GUI/PresetBundle.cpp       |  2 +-
 src/slic3r/GUI/Selection.cpp          |  2 +-
 src/slic3r/GUI/WipeTowerDialog.cpp    |  2 +-
 src/slic3r/GUI/wxExtensions.cpp       |  4 ++--
 24 files changed, 47 insertions(+), 47 deletions(-)

diff --git a/src/clipper/clipper_z.hpp b/src/clipper/clipper_z.hpp
index 0f31ac11c..e5e7d48ce 100644
--- a/src/clipper/clipper_z.hpp
+++ b/src/clipper/clipper_z.hpp
@@ -15,4 +15,4 @@
 #undef clipper_hpp
 #undef use_xyz
 
-#endif clipper_z_hpp
+#endif // clipper_z_hpp
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 577698071..9aab3a0eb 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -249,7 +249,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const
             // Special case: For a DynamicConfig, convert a templated enum to a generic enum.
             new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) :
             this->default_value->clone();
-	return this->create_empty_option();
+    return this->create_empty_option();
 }
 
 // Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index c349ad3e1..334593ab5 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -353,7 +353,7 @@ public:
     bool apply_override(const ConfigOption *rhs) override {
         if (this->nullable())
         	throw std::runtime_error("Cannot override a nullable ConfigOption.");
-		if (rhs->type() != this->type())
+        if (rhs->type() != this->type())
 			throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types.");
 		auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
 		if (! rhs->nullable()) {
@@ -461,7 +461,7 @@ public:
         for (const double &v : this->values) {
             if (&v != &this->values.front())
             	ss << ",";
-        	serialize_single_value(ss, v);
+            serialize_single_value(ss, v);
         }
         return ss.str();
     }
@@ -607,7 +607,7 @@ public:
         for (const int &v : this->values) {
             if (&v != &this->values.front())
             	ss << ",";
-        	serialize_single_value(ss, v);
+            serialize_single_value(ss, v);
         }
         return ss.str();
     }
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 92c958d8a..8989487cc 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -979,7 +979,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
             stream << layer_height_profile.front();
             for (size_t i = 1; i < layer_height_profile.size(); ++i)
                 stream << ";" << layer_height_profile[i];
-                stream << "\n    </metadata>\n";
+            stream << "\n    </metadata>\n";
         }
 
         // Export layer height ranges including the layer range specific config overrides.
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 0ccc3ddf5..d7f432fde 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -659,7 +659,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
     if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path)))
         return;
 
-	print->set_started(psGCodeExport);
+    print->set_started(psGCodeExport);
 
     BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info();
 
@@ -1415,7 +1415,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
         // Skip the rest of the line.
         for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr);
 		// Skip the end of line indicators.
-		for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
+        for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
 	}
     return temp_set_by_gcode;
 }
diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp
index 0a9ec320e..b7d1d57df 100644
--- a/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/src/libslic3r/GCode/ToolOrdering.cpp
@@ -308,7 +308,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
                     LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
                     // Find the 1st layer above lt_new.
                     for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j);
-					if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
+                    if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
 						m_layer_tools[j].has_wipe_tower = true;
 					} else {
 						LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp
index b35761b5f..ea8465f22 100644
--- a/src/libslic3r/GCode/WipeTower.cpp
+++ b/src/libslic3r/GCode/WipeTower.cpp
@@ -698,7 +698,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
         writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str())
               .append(";--------------------\n");
 
-	writer.speed_override_backup();
+    writer.speed_override_backup();
 	writer.speed_override(100);
 
 	Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
@@ -748,7 +748,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
     if (m_current_tool < m_used_filament_length.size())
         m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
 
-	ToolChangeResult result;
+    ToolChangeResult result;
     result.priming      = false;
     result.initial_tool = int(old_tool);
     result.new_tool     = int(m_current_tool);
@@ -806,7 +806,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
     if (m_current_tool < m_used_filament_length.size())
     	m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
 
-	ToolChangeResult result;
+    ToolChangeResult result;
     result.priming      = false;
     result.initial_tool = int(old_tool);
     result.new_tool     = int(m_current_tool);
@@ -1163,7 +1163,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
                 writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
                 writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y());
             }
-            writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
+        writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
     }
     else {  // Extrude a sparse infill to support the material to be printed above.
         const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
@@ -1196,7 +1196,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
     if (m_current_tool < m_used_filament_length.size())
         m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
 
-	ToolChangeResult result;
+    ToolChangeResult result;
     result.priming      = false;
     result.initial_tool = int(old_tool);
     result.new_tool     = int(m_current_tool);
diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp
index 6fc0b4e37..bfe96d311 100644
--- a/src/libslic3r/LayerRegion.cpp
+++ b/src/libslic3r/LayerRegion.cpp
@@ -144,7 +144,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
             }
             if (! lower_layer_covered->empty())
             	voids = diff(voids, *lower_layer_covered);
-        	fill_boundaries = diff(fill_boundaries, voids);
+            fill_boundaries = diff(fill_boundaries, voids);
         }
     }
 
@@ -473,4 +473,4 @@ void LayerRegion::export_region_fill_surfaces_to_svg_debug(const char *name) con
 }
 
 }
- 
\ No newline at end of file
+ 
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 4d8482743..b275af2f2 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -252,7 +252,7 @@ bool Print::is_step_done(PrintObjectStep step) const
 {
     if (m_objects.empty())
         return false;
-	tbb::mutex::scoped_lock lock(this->state_mutex());
+    tbb::mutex::scoped_lock lock(this->state_mutex());
     for (const PrintObject *object : m_objects)
         if (! object->is_step_done_unguarded(step))
             return false;
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 2834d9105..44f1ef875 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1480,7 +1480,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
 
     if (object_max_z <= 0.f)
         object_max_z = (float)model_object.raw_bounding_box().size().z();
-	return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
+    return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
 }
 
 // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 505fdcaf5..46800de9a 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -783,7 +783,7 @@ namespace SupportMaterialInternal {
         for (const ExtrusionPath &ep : loop.paths)
             if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
                 return ep.size() >= (ep.is_closed() ? 3 : 2);
-            return false;
+        return false;
     }
     static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
     {
@@ -2125,7 +2125,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                 }
                 // $layer->slices contains the full shape of layer, thus including
                 // perimeter's width. $support contains the full shape of support
-                // material, thus including the width of its foremost extrusion.
+                // material, thus including the width of its foremost extrusion.
                 // We leave a gap equal to a full extrusion width.
                 support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
             }
@@ -3217,7 +3217,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
                     density = 0.5f;
                     flow = m_first_layer_flow;
                     // use the proper spacing for first layer as we don't need to align
-                    // its pattern to the other layers
+                    // its pattern to the other layers
                     //FIXME When paralellizing, each thread shall have its own copy of the fillers.
                     filler->spacing = flow.spacing();
                     filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp
index 622b31a17..80b6521b6 100644
--- a/src/slic3r/Config/Snapshot.cpp
+++ b/src/slic3r/Config/Snapshot.cpp
@@ -342,7 +342,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src
         ! boost::filesystem::create_directory(path_dst))
         throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string());
 
-	for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
+    for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
         if (Slic3r::is_ini_file(dir_entry))
 		    boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists);
 }
@@ -351,7 +351,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path)
 {
     if (! boost::filesystem::is_directory(path))
     	return;
-	for (auto &dir_entry : boost::filesystem::directory_iterator(path))
+    for (auto &dir_entry : boost::filesystem::directory_iterator(path))
         if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini"))
 		    boost::filesystem::remove(dir_entry.path());
 }
@@ -378,7 +378,7 @@ const Snapshot&	SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
         sprintf(name, "filament_%u", i);
         if (! app_config.has("presets", name))
             break;
-	    snapshot.filaments.emplace_back(app_config.get("presets", name));
+        snapshot.filaments.emplace_back(app_config.get("presets", name));
     }
     // Vendor specific config bundles and installed printers.
     for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &vendor : app_config.vendors()) {
@@ -417,7 +417,7 @@ const Snapshot&	SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
     // Backup the presets.
     for (const char *subdir : { "print", "filament", "printer", "vendor" })
     	copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
-	snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
+    snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
     assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
     m_snapshots.emplace_back(std::move(snapshot));
     return m_snapshots.back();
diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp
index 175abff69..3f8f960f1 100644
--- a/src/slic3r/Config/Version.cpp
+++ b/src/slic3r/Config/Version.cpp
@@ -227,9 +227,9 @@ size_t Index::load(const boost::filesystem::path &path)
 				// End of semver or keyword.
 				break;
     	}
-    	if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
+        if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
     		throw file_parser_error("Invalid keyword or semantic version", path, idx_line);
-		char *value = left_trim(key_end);
+        char *value = left_trim(key_end);
 		bool  key_value_pair = *value == '=';
 		if (key_value_pair)
 			value = left_trim(value + 1);
@@ -245,11 +245,11 @@ size_t Index::load(const boost::filesystem::path &path)
     		if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) {
     			if (! svalue.empty())
 					semver = Semver::parse(svalue);
-		    	if (! semver)
+                if (! semver)
 		    		throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line);
-				if (strcmp(key, "min_slic3r_version") == 0)
+                if (strcmp(key, "min_slic3r_version") == 0)
     				ver.min_slic3r_version = *semver;
-    			else
+                else
     				ver.max_slic3r_version = *semver;
     		} else {
     			// Ignore unknown keys, as there may come new keys in the future.
diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp
index 90fc0ff80..5624ada9d 100644
--- a/src/slic3r/GUI/BedShapeDialog.cpp
+++ b/src/slic3r/GUI/BedShapeDialog.cpp
@@ -455,7 +455,7 @@ void BedShapePanel::update_shape()
     else if (page_idx == SHAPE_CUSTOM) 
         m_shape = m_loaded_shape;
 
-	update_preview();
+    update_preview();
 }
 
 // Loads an stl file, projects it to the XY plane and calculates a polygon.
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 715d9f806..d0ff17a83 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -928,8 +928,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
     if (items_count > 1)
         m_original_height += (items_count - 1) * scaled_square_contour;
 
-	m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
-	m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
+    m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
+    m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
 
     // generates bitmap
     wxBitmap bitmap(m_width, m_height);
@@ -1882,7 +1882,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
     if (m_reload_delayed)
         return;
 
-	bool update_object_list = false;
+    bool update_object_list = false;
 
     if (m_volumes.volumes != glvolumes_new)
 		update_object_list = true;
@@ -5070,14 +5070,14 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
 		    for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers)
 		        for (const ExtrusionPath &path : layer.paths)
 		        	++ num_paths_per_role[size_t(path.role())];
-			std::vector<std::vector<float>> roles_values;
+            std::vector<std::vector<float>> roles_values;
 			roles_values.assign(size_t(erCount), std::vector<float>());
 		    for (size_t i = 0; i < roles_values.size(); ++ i)
 		    	roles_values[i].reserve(num_paths_per_role[i]);
-		    for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
+            for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
 		        for (const ExtrusionPath& path : layer.paths)
 		        	roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path));
-			roles_filters.reserve(size_t(erCount));
+            roles_filters.reserve(size_t(erCount));
 			size_t num_buffers = 0;
 		    for (std::vector<float> &values : roles_values) {
 		    	sort_remove_duplicates(values);
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index f94372667..6e8c361c8 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -125,7 +125,7 @@ void config_wizard(int reason)
     if (! wxGetApp().check_unsaved_changes())
     	return;
 
-	try {
+    try {
 		ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
         wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater);
 	}
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 80c02ea78..86523cb88 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -725,7 +725,7 @@ bool GUI_App::load_language(wxString language, bool initial)
 #endif
         if (initial)
         	message + "\n\nApplication will close.";
-		wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
+        wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
         if (initial)
 			std::exit(EXIT_FAILURE);
 		else
diff --git a/src/slic3r/GUI/LambdaObjectDialog.cpp b/src/slic3r/GUI/LambdaObjectDialog.cpp
index 4d1cb0658..63c8d329c 100644
--- a/src/slic3r/GUI/LambdaObjectDialog.cpp
+++ b/src/slic3r/GUI/LambdaObjectDialog.cpp
@@ -192,7 +192,7 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wx
     else
         panel->SetSizer(optgroup->sizer);
 
-	return optgroup;
+    return optgroup;
 }
 
 
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index dfe3a9cf9..7c36f3665 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -917,7 +917,7 @@ void MainFrame::load_config_file()
 	wxString file;
     if (dlg.ShowModal() == wxID_OK)
         file = dlg.GetPath();
-	if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
+    if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
         wxGetApp().app_config->update_config_dir(get_dir_name(file));
         m_last_config = file;
     }
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index c032aac72..698c1e034 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -133,7 +133,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	full_Label/* = n
         m_options_mode.push_back(option_set[0].opt.mode);
 
 	// if we have a single option with no label, no sidetext just add it directly to sizer
-	if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
+    if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
         option_set.front().opt.label.empty() &&
 		option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && 
 		line.get_extra_widgets().size() == 0) {
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index d9e90333c..5785fd850 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -279,7 +279,7 @@ std::string PresetBundle::load_system_presets()
                 errors_cummulative += "\n";
             }
         }
-	if (first) {
+    if (first) {
 		// No config bundle loaded, reset.
 		this->reset(false);
 	}
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index 40fbbbac6..13d4a7360 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -410,7 +410,7 @@ void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t,
     if (! m_valid)
         return;
 
-	m_mode = mode;
+    m_mode = mode;
     for (unsigned int i : m_list)
         (*m_volumes)[i]->selected = false;
     m_list.clear();
diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp
index 460683f77..539422545 100644
--- a/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -371,7 +371,7 @@ void WipingPanel::toggle_advanced(bool user_action) {
     else
         m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
 
-	(m_advanced ? m_page_advanced : m_page_simple)->Show();
+    (m_advanced ? m_page_advanced : m_page_simple)->Show();
 	(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
 
     m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index bf5500cf4..59406b6e9 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -669,7 +669,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
     if (has_errors)
         root->m_bmp = *m_warning_bmp;
 
-	m_objects.push_back(root);
+    m_objects.push_back(root);
 	// notify control
 	wxDataViewItem child((void*)root);
 	wxDataViewItem parent((void*)NULL);
@@ -720,7 +720,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
         root->SetBitmap(*m_warning_bmp);
 
 	// notify control
-	const wxDataViewItem child((void*)node);
+    const wxDataViewItem child((void*)node);
     ItemAdded(parent_item, child);
     root->m_volumes_cnt++;
 

From b67d8c1614796394a217e9c1b27a57b8f2a88cbf Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 24 Sep 2019 17:09:07 +0200
Subject: [PATCH 26/44] Fixed memsetting non-trivially-copyable types

Types stl_stats, stl_normal and SurfaceFillParams should not be zeroed by memset
This is not correct and also triggered warnings on gcc
---
 src/admesh/stl.h              | 57 ++++++++++++++++++-----------------
 src/libslic3r/Fill/Fill.cpp   | 25 ++++++++-------
 src/libslic3r/Format/PRUS.cpp |  2 +-
 src/libslic3r/Model.cpp       |  2 +-
 4 files changed, 43 insertions(+), 43 deletions(-)

diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 117090dd8..43999d365 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -89,33 +89,34 @@ struct stl_neighbors {
 };
 
 struct stl_stats {
-	stl_stats() { this->reset(); }
-	void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; }
-	char          header[81];
-	stl_type      type;
-	uint32_t      number_of_facets;
-	stl_vertex    max;
-	stl_vertex    min;
-	stl_vertex    size;
-	float         bounding_diameter;
-	float         shortest_edge;
-	float         volume;
-	int           connected_edges;
-	int           connected_facets_1_edge;
-	int           connected_facets_2_edge;
-	int           connected_facets_3_edge;
-	int           facets_w_1_bad_edge;
-	int           facets_w_2_bad_edge;
-	int           facets_w_3_bad_edge;
-	int           original_num_facets;
-	int           edges_fixed;
-	int           degenerate_facets;
-	int           facets_removed;
-	int           facets_added;
-	int           facets_reversed;
-	int           backwards_edges;
-	int           normals_fixed;
-	int           number_of_parts;
+    stl_stats() { memset(&header, 0, 81); }
+    char          header[81]                = "";
+    stl_type      type                      = (stl_type)0;
+    uint32_t      number_of_facets          = 0;
+    stl_vertex    max                       = stl_vertex::Zero();
+    stl_vertex    min                       = stl_vertex::Zero();
+    stl_vertex    size                      = stl_vertex::Zero();
+    float         bounding_diameter         = 0.f;
+    float         shortest_edge             = 0.f;
+    float         volume                    = -1.f;
+    int           connected_edges           = 0;
+    int           connected_facets_1_edge   = 0;
+    int           connected_facets_2_edge   = 0;
+    int           connected_facets_3_edge   = 0;
+    int           facets_w_1_bad_edge       = 0;
+    int           facets_w_2_bad_edge       = 0;
+    int           facets_w_3_bad_edge       = 0;
+    int           original_num_facets       = 0;
+    int           edges_fixed               = 0;
+    int           degenerate_facets         = 0;
+    int           facets_removed            = 0;
+    int           facets_added              = 0;
+    int           facets_reversed           = 0;
+    int           backwards_edges           = 0;
+    int           normals_fixed             = 0;
+    int           number_of_parts           = 0;
+
+    void clear() { *this = stl_stats(); }
 };
 
 struct stl_file {
@@ -124,7 +125,7 @@ struct stl_file {
 	void clear() {
 		this->facet_start.clear();
 		this->neighbors_start.clear();
-		this->stats.reset();
+        this->stats.clear();
 	}
 
 	size_t memsize() const {
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index ad308adab..ab20bbddb 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -15,40 +15,39 @@ namespace Slic3r {
 
 struct SurfaceFillParams
 {
-	SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); }
 	// Zero based extruder ID.
-	unsigned int 	extruder;
+    unsigned int 	extruder = 0;
 	// Infill pattern, adjusted for the density etc.
-	InfillPattern  	pattern;
+    InfillPattern  	pattern = InfillPattern(0);
 
     // FillBase
     // in unscaled coordinates
-    coordf_t    	spacing;
+    coordf_t    	spacing = 0.;
     // infill / perimeter overlap, in unscaled coordinates
-    coordf_t    	overlap;
+    coordf_t    	overlap = 0.;
     // Angle as provided by the region config, in radians.
-    float       	angle;
+    float       	angle = 0.f;
     // Non-negative for a bridge.
-    float 			bridge_angle;
+    float 			bridge_angle = 0.f;
 
     // FillParams
-    float       	density;
+    float       	density = 0.f;
     // Don't connect the fill lines around the inner perimeter.
-    bool        	dont_connect;
+    bool        	dont_connect = false;
     // Don't adjust spacing to fill the space evenly.
-    bool        	dont_adjust;
+    bool        	dont_adjust = false;
 
     // width, height of extrusion, nozzle diameter, is bridge
     // For the output, for fill generator.
-	Flow 			flow;
+    Flow 			flow = Flow(0.f, 0.f, 0.f, false);
 
 	// For the output
-	ExtrusionRole	extrusion_role;
+    ExtrusionRole	extrusion_role = ExtrusionRole(0);
 
 	// Various print settings?
 
 	// Index of this entry in a linear vector.
-	size_t 			idx;
+    size_t 			idx = 0;
 
 
 	bool operator<(const SurfaceFillParams &rhs) const {
diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp
index 03ea71a83..63812929d 100644
--- a/src/libslic3r/Format/PRUS.cpp
+++ b/src/libslic3r/Format/PRUS.cpp
@@ -246,7 +246,7 @@ static void extract_model_from_archive(
                 sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
                 // Normal was mangled. Maybe denormals or "not a number" were stored?
                 // Just reset the normal and silently ignore it.
-                memset(&facet.normal, 0, sizeof(facet.normal));
+                facet.normal = stl_normal::Zero();
             }
             facets.emplace_back(facet);
         }
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 7526dd16a..1e06f0703 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -1462,7 +1462,7 @@ stl_stats ModelObject::get_object_stl_stats() const
         return this->volumes[0]->mesh().stl.stats;
 
     stl_stats full_stats;
-    memset(&full_stats, 0, sizeof(stl_stats));
+    full_stats.volume = 0.f;
 
     // fill full_stats from all objet's meshes
     for (ModelVolume* volume : this->volumes)

From 1d18339e62cbc6eabd3fe6d409500cfb10649143 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 24 Sep 2019 18:09:48 +0200
Subject: [PATCH 27/44] PRUS.cpp: Fixed an always-true condition in
 extract_model_from_archive SupportMaterial.cpp: Simplified one condition
 (also solves a -Wmaybe-uninitialized warning on gcc)

---
 src/libslic3r/Format/PRUS.cpp     |  2 +-
 src/libslic3r/SupportMaterial.cpp | 15 ++++-----------
 2 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp
index 63812929d..d6f87197d 100644
--- a/src/libslic3r/Format/PRUS.cpp
+++ b/src/libslic3r/Format/PRUS.cpp
@@ -278,7 +278,7 @@ static void extract_model_from_archive(
         instance->set_rotation(instance_rotation);
         instance->set_scaling_factor(instance_scaling_factor);
         instance->set_offset(instance_offset);
-        if (group_id != (size_t)-1)
+        if (group_id != (unsigned int)(-1))
             group_to_model_object[group_id] = model_object;
     } else {
         // This is not the 1st mesh of a group. Add it to the ModelObject.
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 46800de9a..c0c8e9ea0 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -2934,20 +2934,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
     // Prepare fillers.
     SupportMaterialPattern  support_pattern = m_object_config->support_material_pattern;
     bool                    with_sheath     = m_object_config->support_material_with_sheath;
-    InfillPattern           infill_pattern;
+    InfillPattern           infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear);
     std::vector<float>      angles;
     angles.push_back(base_angle);
-    switch (support_pattern) {
-    case smpRectilinearGrid:
+
+    if (support_pattern == smpRectilinearGrid)
         angles.push_back(interface_angle);
-        // fall through
-    case smpRectilinear:
-        infill_pattern = ipRectilinear;
-        break;
-    case smpHoneycomb:
-        infill_pattern = ipHoneycomb;
-        break;
-    }
+
     BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.)));
 
 //    const coordf_t link_max_length_factor = 3.;

From fdbe3396225bc2830a8a7de86efc405d40282ac0 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Wed, 25 Sep 2019 11:25:14 +0200
Subject: [PATCH 28/44] Fixed gizmo scale for objects added to scene using the
 new context menu

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index b7882a059..19dedc7b0 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -1901,6 +1901,9 @@ void ObjectList::load_shape_object(const std::string& type_name)
     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
     new_object->invalidate_bounding_box();
 
+    new_object->center_around_origin();
+    new_object->ensure_on_bed();
+
     const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
     new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));
 

From dbc51a140d26e36480b74252b7b5a7a24d0e17f6 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 25 Sep 2019 12:06:38 +0200
Subject: [PATCH 29/44] Fixed erroneous use of logical OR where bitwise OR was
 meant Also, PrintBase::SlicingStatus enum had two entries assigned to 0 -
 removed the one that was never used

---
 src/libslic3r/PrintBase.hpp | 3 +--
 src/slic3r/GUI/Plater.cpp   | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp
index aebc87904..05d884cc8 100644
--- a/src/libslic3r/PrintBase.hpp
+++ b/src/libslic3r/PrintBase.hpp
@@ -268,8 +268,7 @@ public:
         std::string     text;
         // Bitmap of flags.
         enum FlagBits {
-            DEFAULT,
-            NO_RELOAD_SCENE                 = 0,
+            DEFAULT                         = 0,
             RELOAD_SCENE                    = 1 << 1,
             RELOAD_SLA_SUPPORT_POINTS       = 1 << 2,
             RELOAD_SLA_PREVIEW              = 1 << 3,
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index c7f1f4c5c..97c292703 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3322,7 +3322,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
         this->statusbar()->set_progress(evt.status.percent);
         this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("â€Ĥ"));
     }
-    if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
+    if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
         switch (this->printer_technology) {
         case ptFFF:
             this->update_fff_scene();

From 6f8a9bc1fff64cb976a0a7a8bdf05bba289e5923 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Wed, 25 Sep 2019 13:05:18 +0200
Subject: [PATCH 30/44] Fixed visual hints in 3D scene not disappearing when
 tabbing out of size z field in object manipulators sidebar

---
 src/slic3r/GUI/GLCanvas3D.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index d0ff17a83..5b1188169 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3389,10 +3389,9 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
     m_sidebar_field = focus_on ? opt_key : "";
 
     if (!m_sidebar_field.empty())
-    {
         m_gizmos.reset_all_states();
-        m_dirty = true;
-    }
+
+    m_dirty = true;
 }
 
 void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type)

From 41495a932a94a64b949a0950d0e8be445e9083e9 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 26 Sep 2019 09:44:38 +0200
Subject: [PATCH 31/44] Introduction of a greedy Traveling Salesman Problem
 algorithm, producing better shortest path estimate than the "closest next
 neighbor" heuristics. The new greedy algorithm utilizes KD tree for closest
 end point search, and builds a graph to detect loops.

PerimeterGenerator newly uses the optimized TSP algorithm.

ExtrusionEntity has been refactored / simplified.
---
 src/libslic3r/CMakeLists.txt                |   4 +-
 src/libslic3r/ExtrusionEntityCollection.cpp |  17 +-
 src/libslic3r/ExtrusionEntityCollection.hpp |  11 +-
 src/libslic3r/KDTreeIndirect.hpp            | 228 ++++++++++
 src/libslic3r/MutablePriorityQueue.hpp      |  31 +-
 src/libslic3r/PerimeterGenerator.cpp        |  71 +--
 src/libslic3r/ShortestPath.cpp              | 479 ++++++++++++++++++++
 src/libslic3r/ShortestPath.hpp              |  17 +
 8 files changed, 797 insertions(+), 61 deletions(-)
 create mode 100644 src/libslic3r/KDTreeIndirect.hpp
 create mode 100644 src/libslic3r/ShortestPath.cpp
 create mode 100644 src/libslic3r/ShortestPath.hpp

diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 97e0fc09b..6fe68e56f 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -100,7 +100,7 @@ add_library(libslic3r STATIC
     Geometry.cpp
     Geometry.hpp
     Int128.hpp
-#    KdTree.hpp
+    KdTreeIndirect.hpp
     Layer.cpp
     Layer.hpp
     LayerRegion.cpp
@@ -142,6 +142,8 @@ add_library(libslic3r STATIC
     PrintObject.cpp
     PrintRegion.cpp
     Semver.cpp
+    ShortestPath.cpp
+    ShortestPath.hpp
     SLAPrint.cpp
     SLAPrint.hpp
     SLA/SLAAutoSupports.hpp
diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp
index 70c2348af..f7fab1ba1 100644
--- a/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -16,7 +16,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE
     this->entities      = other.entities;
     for (size_t i = 0; i < this->entities.size(); ++i)
         this->entities[i] = this->entities[i]->clone();
-    this->orig_indices  = other.orig_indices;
     this->no_sort       = other.no_sort;
     return *this;
 }
@@ -24,7 +23,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE
 void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c)
 {
     std::swap(this->entities, c.entities);
-    std::swap(this->orig_indices, c.orig_indices);
     std::swap(this->no_sort, c.no_sort);
 }
 
@@ -82,10 +80,10 @@ ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_revers
     return coll;
 }
 
-void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const
+void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
 {
     if (this->entities.empty()) return;
-    this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices);
+    this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role);
 }
 
 ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
@@ -95,7 +93,7 @@ ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point sta
     return coll;
 }
 
-void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const
+void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
 {
     if (this->no_sort) {
         *retval = *this;
@@ -103,7 +101,6 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
     }
 
     retval->entities.reserve(this->entities.size());
-    retval->orig_indices.reserve(this->entities.size());
     
     // if we're asked to return the original indices, build a map
     std::map<ExtrusionEntity*,size_t> indices_map;
@@ -122,8 +119,8 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
 
         ExtrusionEntity *entity = entity_src->clone();
         my_paths.push_back(entity);
-        if (orig_indices != nullptr)
-        	indices_map[entity] = &entity_src - &this->entities.front();
+//        if (orig_indices != nullptr)
+//        	indices_map[entity] = &entity_src - &this->entities.front();
     }
     
     Points endpoints;
@@ -142,8 +139,8 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
         if (start_index % 2 && !no_reverse && entity->can_reverse())
             entity->reverse();
         retval->entities.push_back(my_paths.at(path_index));
-        if (orig_indices != nullptr)
-        	orig_indices->push_back(indices_map[entity]);
+//        if (orig_indices != nullptr)
+//        	orig_indices->push_back(indices_map[entity]);
         my_paths.erase(my_paths.begin() + path_index);
         endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
         start_near = retval->entities.back()->last_point();
diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp
index 221afc453..e92fa156f 100644
--- a/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -14,15 +14,14 @@ public:
 	ExtrusionEntity* clone_move() override { return new ExtrusionEntityCollection(std::move(*this)); }
 
     ExtrusionEntitiesPtr entities;     // we own these entities
-    std::vector<size_t> orig_indices;  // handy for XS
     bool no_sort;
     ExtrusionEntityCollection(): no_sort(false) {};
-    ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : orig_indices(other.orig_indices), no_sort(other.no_sort) { this->append(other.entities); }
-    ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), orig_indices(std::move(other.orig_indices)), no_sort(other.no_sort) {}
+    ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); }
+    ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {}
     explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
     ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
     ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) 
-        { this->entities = std::move(other.entities); this->orig_indices = std::move(other.orig_indices); this->no_sort = other.no_sort; return *this; }
+        { this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; }
     ~ExtrusionEntityCollection() { clear(); }
     explicit operator ExtrusionPaths() const;
     
@@ -67,9 +66,9 @@ public:
     void replace(size_t i, const ExtrusionEntity &entity);
     void remove(size_t i);
     ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
-    void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector<size_t>* orig_indices = nullptr) const;
+    void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
     ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const;
-    void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector<size_t>* orig_indices = nullptr) const;
+    void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
     void reverse();
     Point first_point() const { return this->entities.front()->first_point(); }
     Point last_point() const { return this->entities.back()->last_point(); }
diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp
new file mode 100644
index 000000000..f79dab9b3
--- /dev/null
+++ b/src/libslic3r/KDTreeIndirect.hpp
@@ -0,0 +1,228 @@
+// KD tree built upon external data set, referencing the external data by integer indices.
+
+#ifndef slic3r_KDTreeIndirect_hpp_
+#define slic3r_KDTreeIndirect_hpp_
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "Utils.hpp" // for next_highest_power_of_2()
+
+namespace Slic3r {
+
+// KD tree for N-dimensional closest point search.
+template<size_t ANumDimensions, typename ACoordType, typename ACoordinateFn>
+class KDTreeIndirect
+{
+public:
+	static constexpr size_t NumDimensions = ANumDimensions;
+	using					CoordinateFn  = ACoordinateFn;
+	using					CoordType     = ACoordType;
+	static constexpr size_t npos		  = size_t(-1);
+
+	KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
+	KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t>   indices) : coordinate(coordinate) { this->build(std::move(indices)); }
+	KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> &&indices) : coordinate(coordinate) { this->build(std::move(indices)); }
+	KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
+	KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
+	KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
+	void clear() { m_nodes.clear(); }
+
+	void build(size_t num_indices)
+	{
+		std::vector<size_t> indices;
+		indices.reserve(num_indices);
+		for (size_t i = 0; i < num_indices; ++ i)
+			indices.emplace_back(i);
+		this->build(std::move(indices));
+	}
+
+	void build(std::vector<size_t> &&indices)
+	{
+		if (indices.empty())
+			clear();
+		else {
+			// Allocate a next highest power of 2 nodes, because the incomplete binary tree will not have the leaves filled strictly from the left.
+			m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos);
+			build_recursive(indices, 0, 0, 0, (int)(indices.size() - 1));
+		}
+		indices.clear();
+	}
+
+	enum class VisitorReturnMask : unsigned int
+	{
+		CONTINUE_LEFT   = 1,
+		CONTINUE_RIGHT  = 2,
+		STOP 			= 4,
+	};
+	template<typename CoordType> 
+	unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
+	{
+		CoordType dist = point_coord - this->coordinate(idx, dimension);
+		return (dist * dist < search_radius + CoordType(EPSILON)) ?
+			((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
+			(dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
+	}
+
+	// Visitor is supposed to return a bit mask of VisitorReturnMask.
+	template<typename Visitor>
+	void visit(Visitor &visitor) const
+	{
+		return m_nodes.empty() ? npos : visit_recursive(0, 0, visitor);
+	}
+
+	CoordinateFn coordinate;
+
+private:
+	// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
+	void build_recursive(std::vector<size_t> &input, size_t node, int dimension, int left, int right)
+	{
+		if (left > right)
+			return;
+
+		assert(node < m_nodes.size());
+
+		if (left == right) {
+			// Insert a node into the balanced tree.
+			m_nodes[node] = input[left];
+			return;
+		}
+
+		// Partition the input sequence to two equal halves.
+		int center = (left + right) >> 1;
+		partition_input(input, dimension, left, right, center);
+		// Insert a node into the tree.
+		m_nodes[node] = input[center];
+		// Partition the left and right subtrees.
+		size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
+		build_recursive(input, (node << 1) + 1, next_dimension, left,	    center - 1);
+		build_recursive(input, (node << 1) + 2, next_dimension, center + 1, right);
+	}
+
+	// Partition the input m_nodes <left, right> at k using QuickSelect method.
+	// https://en.wikipedia.org/wiki/Quickselect
+	void partition_input(std::vector<size_t> &input, int dimension, int left, int right, int k) const
+	{
+		while (left < right) {
+			// Guess the k'th element.
+			// Pick the pivot as a median of first, center and last value.
+			// Sort first, center and last values.
+			int  center       = (left + right) >> 1;
+			auto left_value   = this->coordinate(input[left],   dimension);
+			auto center_value = this->coordinate(input[center], dimension);
+			auto right_value  = this->coordinate(input[right],  dimension);
+			if (center_value < left_value) {
+				std::swap(input[left], input[center]);
+				std::swap(left_value,  center_value);
+			}
+			if (right_value < left_value) {
+				std::swap(input[left], input[right]);
+				std::swap(left_value,  right_value);
+			}
+			if (right_value < center_value) {
+				std::swap(input[center], input[right]);
+				// No need to do that, result is not used.
+				// std::swap(center_value,  right_value);
+			}
+			// Only two or three values are left and those are sorted already.
+			if (left + 3 > right)
+				break;
+			// left and right items are already at their correct positions.
+			// input[left].point[dimension] <= input[center].point[dimension] <= input[right].point[dimension]
+			// Move the pivot to the (right - 1) position.
+			std::swap(input[center], input[right - 1]);
+			// Pivot value.
+			double pivot = this->coordinate(input[right - 1],  dimension);
+			// Partition the set based on the pivot.
+			int i = left;
+			int j = right - 1;
+			for (;;) {
+				// Skip left points that are already at correct positions.
+				// Search will certainly stop at position (right - 1), which stores the pivot.
+				while (this->coordinate(input[++ i], dimension) < pivot) ;
+				// Skip right points that are already at correct positions.
+				while (this->coordinate(input[-- j], dimension) > pivot && i < j) ;
+				if (i >= j)
+					break;
+				std::swap(input[i], input[j]);
+			}
+			// Restore pivot to the center of the sequence.
+			std::swap(input[i], input[right]);
+			// Which side the kth element is in?
+			if (k < i)
+				right = i - 1;
+			else if (k == i)
+				// Sequence is partitioned, kth element is at its place.
+				break;
+			else
+				left = i + 1;
+		}
+	}
+
+	template<typename Visitor>
+	void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const
+	{
+		assert(! m_nodes.empty());
+		if (node >= m_nodes.size() || m_nodes[node] == npos)
+			return;
+
+		// Left / right child node index.
+		size_t left  = (node << 1) + 1;
+		size_t right = left + 1;
+		unsigned int mask = visitor(m_nodes[node], dimension);
+		if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) {
+			size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
+			if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT)
+				visit_recursive(left,  next_dimension, visitor);
+			if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT)
+				visit_recursive(right, next_dimension, visitor);
+		}
+	}
+
+	std::vector<size_t> m_nodes;
+};
+
+// Find a closest point using Euclidian metrics.
+// Returns npos if not found.
+template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
+size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter)
+{
+	struct Visitor {
+		using CoordType = typename KDTreeIndirectType::CoordType;
+		const KDTreeIndirectType   &kdtree;
+		const PointType    		   &point;
+		const FilterFn				filter;
+		size_t 						min_idx  = KDTreeIndirectType::npos;
+		CoordType					min_dist = std::numeric_limits<CoordType>::max();
+
+		Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {}
+		unsigned int operator()(size_t idx, size_t dimension) {
+			if (this->filter(idx)) {
+				auto dist = CoordType(0);
+				for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) {
+					CoordType d = point[i] - kdtree.coordinate(idx, i);
+					dist += d * d;
+				}
+				if (dist < min_dist) {
+					min_dist = dist;
+					min_idx  = idx;
+				}
+			}
+			return kdtree.descent_mask(point[dimension], min_dist, idx, dimension);
+		}
+	} visitor(kdtree, point, filter);
+
+	kdtree.visit(visitor);
+	return visitor.min_idx;
+}
+
+template<typename KDTreeIndirectType, typename PointType>
+size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point)
+{
+	return find_closest_point(kdtree, point, [](size_t) { return true; });
+}
+
+} // namespace Slic3r
+
+#endif /* slic3r_KDTreeIndirect_hpp_ */
diff --git a/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp
index 82e992fd6..31946c707 100644
--- a/src/libslic3r/MutablePriorityQueue.hpp
+++ b/src/libslic3r/MutablePriorityQueue.hpp
@@ -13,21 +13,28 @@ public:
 		{}
 	~MutablePriorityQueue()	{ clear(); }
 
-	inline void		clear()								{ m_heap.clear(); }
-	inline void		reserve(size_t cnt) 				{ m_heap.reserve(cnt); }
-	inline void		push(const T &item);
-	inline void		push(T &&item);
-	inline void		pop();
-	inline T&		top()								{ return m_heap.front(); }
-	inline void		remove(size_t idx);
-	inline void		update(size_t idx) 					{ T item = m_heap[idx]; remove(idx); push(item); }
+	void		clear()								{ m_heap.clear(); }
+	void		reserve(size_t cnt) 				{ m_heap.reserve(cnt); }
+	void		push(const T &item);
+	void		push(T &&item);
+	void		pop();
+	T&			top()								{ return m_heap.front(); }
+	void		remove(size_t idx);
+	void		update(size_t idx) 					{ T item = m_heap[idx]; remove(idx); push(item); }
 
-	inline size_t	size() const						{ return m_heap.size(); }
-	inline bool		empty() const						{ return m_heap.empty(); }
+	size_t		size() const						{ return m_heap.size(); }
+	bool		empty() const						{ return m_heap.empty(); }
+
+	using iterator		 = typename std::vector<T>::iterator;
+	using const_iterator = typename std::vector<T>::const_iterator;
+	iterator 		begin() 		{ return m_heap.begin(); }
+	iterator 		end() 			{ return m_heap.end(); }
+	const_iterator 	cbegin() const	{ return m_heap.cbegin(); }
+	const_iterator 	cend() const	{ return m_heap.cend(); }
 
 protected:
-	inline void		update_heap_up(size_t top, size_t bottom);
-	inline void		update_heap_down(size_t top, size_t bottom);
+	void		update_heap_up(size_t top, size_t bottom);
+	void		update_heap_down(size_t top, size_t bottom);
 
 private:
 	std::vector<T>	m_heap;
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index 0c16f4a1d..cd5354776 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -1,6 +1,8 @@
 #include "PerimeterGenerator.hpp"
 #include "ClipperUtils.hpp"
 #include "ExtrusionEntityCollection.hpp"
+#include "ShortestPath.hpp"
+
 #include <cmath>
 #include <cassert>
 
@@ -86,24 +88,24 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi
     return paths;
 }
 
-static ExtrusionEntityCollection variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow)
+static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow, std::vector<ExtrusionEntity*> &out)
 {
 	// This value determines granularity of adaptive width, as G-code does not allow
 	// variable extrusion within a single move; this value shall only affect the amount
 	// of segments, and any pruning shall be performed before we apply this tolerance.
-	ExtrusionEntityCollection coll;
 	const float tolerance = float(scale_(0.05));
 	for (const ThickPolyline &p : polylines) {
 		ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance);
 		// Append paths to collection.
 		if (! paths.empty()) {
 			if (paths.front().first_point() == paths.back().last_point())
-				coll.append(ExtrusionLoop(std::move(paths)));
-			else
-				coll.append(std::move(paths));
+				out.emplace_back(new ExtrusionLoop(std::move(paths)));
+			else {
+				for (ExtrusionPath &path : paths)
+					out.emplace_back(new ExtrusionPath(std::move(path)));
+			}
 		}
 	}
-	return coll;
 }
 
 // Hierarchy of perimeters.
@@ -186,43 +188,47 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
             paths.push_back(path);
         }
         
-        coll.append(ExtrusionLoop(paths, loop_role));
+        coll.append(ExtrusionLoop(std::move(paths), loop_role));
     }
     
     // Append thin walls to the nearest-neighbor search (only for first iteration)
     if (! thin_walls.empty()) {
-        ExtrusionEntityCollection tw = variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow);
-        coll.append(tw.entities);
+        variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow, coll.entities);
         thin_walls.clear();
     }
     
-    // Sort entities into a new collection using a nearest-neighbor search,
-    // preserving the original indices which are useful for detecting thin walls.
-    ExtrusionEntityCollection sorted_coll;
-    coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices);
-    
-    // traverse children and build the final collection
-    ExtrusionEntityCollection entities;
-    for (const size_t &idx : sorted_coll.orig_indices) {
-        if (idx >= loops.size()) {
-            // This is a thin wall. Let's get it from the sorted collection as it might have been reversed.
-            entities.append(std::move(*sorted_coll.entities[&idx - &sorted_coll.orig_indices.front()]));
+    // Traverse children and build the final collection.
+	Point zero_point(0, 0);
+	std::vector<std::pair<size_t, bool>> chain = chain_extrusion_entities(coll.entities, &zero_point);
+    ExtrusionEntityCollection out;
+    for (const std::pair<size_t, bool> &idx : chain) {
+		assert(coll.entities[idx.first] != nullptr);
+        if (idx.first >= loops.size()) {
+            // This is a thin wall.
+			out.entities.reserve(out.entities.size() + 1);
+            out.entities.emplace_back(coll.entities[idx.first]);
+			coll.entities[idx.first] = nullptr;
+            if (idx.second)
+				out.entities.back()->reverse();
         } else {
-            const PerimeterGeneratorLoop &loop = loops[idx];
-            ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[idx]);
+            const PerimeterGeneratorLoop &loop = loops[idx.first];
+            assert(thin_walls.empty());
             ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
+            out.entities.reserve(out.entities.size() + children.entities.size() + 1);
+            ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
+            coll.entities[idx.first] = nullptr;
             if (loop.is_contour) {
-                eloop.make_counter_clockwise();
-                entities.append(std::move(children.entities));
-                entities.append(std::move(eloop));
+                eloop->make_counter_clockwise();
+                out.append(std::move(children.entities));
+                out.entities.emplace_back(eloop);
             } else {
-                eloop.make_clockwise();
-                entities.append(std::move(eloop));
-                entities.append(std::move(children.entities));
+                eloop->make_clockwise();
+                out.entities.emplace_back(eloop);
+                out.append(std::move(children.entities));
             }
         }
     }
-    return entities;
+    return out;
 }
 
 void PerimeterGenerator::process()
@@ -445,8 +451,8 @@ void PerimeterGenerator::process()
             for (const ExPolygon &ex : gaps_ex)
                 ex.medial_axis(max, min, &polylines);
             if (! polylines.empty()) {
-                ExtrusionEntityCollection gap_fill = variable_width(polylines, erGapFill, this->solid_infill_flow);
-                this->gap_fill->append(gap_fill.entities);
+				ExtrusionEntityCollection gap_fill;
+				variable_width(polylines, erGapFill, this->solid_infill_flow, gap_fill.entities);
                 /*  Make sure we don't infill narrow parts that are already gap-filled
                     (we only consider this surface's gaps to reduce the diff() complexity).
                     Growing actual extrusions ensures that gaps not filled by medial axis
@@ -456,7 +462,8 @@ void PerimeterGenerator::process()
                 //FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
                 // therefore it may cover the area, but no the volume.
                 last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f));
-            }
+				this->gap_fill->append(std::move(gap_fill.entities));
+			}
         }
 
         // create one more offset to be used as boundary for fill
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
new file mode 100644
index 000000000..e98b0a58c
--- /dev/null
+++ b/src/libslic3r/ShortestPath.cpp
@@ -0,0 +1,479 @@
+#include "ShortestPath.hpp"
+#include "KDTreeIndirect.hpp"
+#include "MutablePriorityQueue.hpp"
+
+#if 0
+	#undef NDEBUG
+	#undef assert
+#endif
+
+#include <cmath>
+#include <cassert>
+
+namespace Slic3r {
+
+// Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm.
+// Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments.
+// Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle.
+// Return index and "reversed" flag.
+// https://en.wikipedia.org/wiki/Multi-fragment_algorithm
+// The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which 
+// is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates 
+// a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities.
+std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
+{
+	std::vector<std::pair<size_t, bool>> out;
+
+	if (entities.empty()) {
+		// Nothing to do.
+	} 
+	else if (entities.size() == 1)
+	{
+		// Just sort the end points so that the first point visited is closest to start_near.
+		ExtrusionEntity *extrusion_entity = entities.front();
+		out.emplace_back(0, extrusion_entity->can_reverse() && start_near != nullptr && 
+			(extrusion_entity->last_point() - *start_near).cast<double>().squaredNorm() < (extrusion_entity->first_point() - *start_near).cast<double>().squaredNorm());
+	} 
+	else
+	{
+		// End points of entities for the KD tree closest point search.
+		// A single end point is inserted into the search structure for loops, two end points are entered for open paths.
+		struct EndPoint {
+			EndPoint(const Vec2d &pos) : pos(pos) {}
+
+			Vec2d     pos;
+
+			// Identifier of the chain, to which this end point belongs. Zero means unassigned.
+			size_t    chain_id = 0;
+			// Link to the closest currently valid end point.
+			EndPoint *edge_out = nullptr;
+			// Reverse of edge_out. As there may be multiple end points with the same edge_out,
+			// these other edge_in points are chained using the on_circle_prev / on_circle_next cyclic loop.
+			EndPoint *edge_in  = nullptr;
+			EndPoint* on_circle_prev = nullptr;
+			EndPoint* on_circle_next = nullptr;
+			void 	  on_circle_merge(EndPoint *other)
+			{
+				EndPoint *a = this;
+				EndPoint *b = other;
+				assert(a->validate());
+				assert(b->validate());
+				if (a->on_circle_next == nullptr)
+					std::swap(a, b);
+				if (a->on_circle_next == nullptr) {
+					a->on_circle_next = a->on_circle_prev = b;
+					b->on_circle_next = b->on_circle_prev = a;
+				} else if (b->on_circle_next == nullptr) {
+					b->on_circle_next = a;
+					b->on_circle_prev = a->on_circle_prev;
+					a->on_circle_prev = b;
+					b->on_circle_prev->on_circle_next = b;
+				} else {
+					EndPoint *next = a->on_circle_next;
+					EndPoint *prev = b->on_circle_prev;
+					a->on_circle_next = b;
+					b->on_circle_prev = a;
+					prev->on_circle_next = next;
+					next->on_circle_prev = prev;
+				}
+				assert(this->validate());
+			}
+			void 	  on_circle_detach() 
+			{
+				if (this->on_circle_next) {
+					EndPoint *next = this->on_circle_next;
+					EndPoint *prev = this->on_circle_prev;
+					if (prev == next) {
+						next->on_circle_next = nullptr;
+						next->on_circle_prev = nullptr;
+					} else {
+						prev->on_circle_next = next;
+						next->on_circle_prev = prev;
+					}
+					assert(prev->validate());
+					assert(next->validate());
+					this->on_circle_next = this->on_circle_prev = nullptr;
+				}
+				assert(this->validate());
+			}
+			bool 	  on_circle_empty() const
+			{
+				assert((this->on_circle_prev == nullptr) == (this->on_circle_next == nullptr));
+				assert(this->on_circle_prev == nullptr || (this->on_circle_prev != this && this->on_circle_next != this));
+				return this->on_circle_next == nullptr;
+			}
+
+#ifndef NDEBUG
+			bool	  validate() 
+			{
+				assert((this->on_circle_prev == nullptr) == (this->on_circle_next == nullptr));
+				assert(this->on_circle_prev == nullptr || (this->on_circle_prev != this && this->on_circle_next != this));
+				assert(this->edge_out == nullptr || edge_out->edge_in != nullptr);
+				assert(this->distance_out >= 0.);
+				assert(this->edge_in == nullptr || this->edge_in->edge_out == this);
+				// Point which is a member of path (chain_id > 0) must not be in circle of some edge_in.
+				assert(this->chain_id == 0 || this->on_circle_empty());
+				if (! this->on_circle_empty()) {
+					// Iterate over the cycle and validate the loop.
+					std::set<const EndPoint*> visited;
+					const EndPoint *ep = this;
+					bool edge_in_found = false;
+					do {
+						// This end point is visited for the first time.
+						assert(visited.insert(ep).second);
+						assert(ep->on_circle_next != ep);
+						assert(ep->on_circle_prev != ep);
+						assert(ep->on_circle_next->on_circle_prev == ep);
+						assert(ep->on_circle_prev->on_circle_next == ep);
+						assert(ep->edge_out != nullptr && ep->edge_out == this->edge_out);
+						if (ep->edge_out->edge_in == ep)
+							edge_in_found = true;
+						ep = ep->on_circle_next;
+					} while (ep != this);
+					assert(edge_in_found);
+				}
+				return true;
+			}
+#endif /* NDEBUG */
+
+			// Distance to the next end point following the link.
+			// Zero value -> start of the final path.
+			double    distance_out = std::numeric_limits<double>::max();
+			size_t    heap_idx = std::numeric_limits<size_t>::max();
+		};
+	    std::vector<EndPoint> end_points;
+	    end_points.reserve(entities.size() * 2);
+	    for (const ExtrusionEntity* const &entity : entities) {
+	    	end_points.emplace_back(entity->first_point().cast<double>());
+	    	end_points.emplace_back(entity->last_point().cast<double>());
+	    }
+
+	    // Construct the closest point KD tree over end points of extrusion entities.
+		auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; };
+		KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size());
+
+		// Helper to detect loops in already connected paths.
+		// Unique chain IDs are assigned to paths. If paths are connected, end points will not have their chain IDs updated, but the chain IDs
+		// will remember an "equivalent" chain ID, which is the lowest ID of all the IDs in the path, and the lowest ID is equivalent to itself.
+		class EquivalentChains {
+		public:
+			// Zero'th chain ID is invalid.
+			EquivalentChains(size_t reserve) { m_equivalent_with.reserve(reserve); m_equivalent_with.emplace_back(0); }
+			// Generate next equivalence class.
+			size_t 				next() {
+				m_equivalent_with.emplace_back(++ m_last_chain_id);
+				return m_last_chain_id;
+			}
+			// Get equivalence class for chain ID.
+			size_t 				operator()(size_t chain_id) {
+				if (chain_id != 0) {
+					for (size_t last = chain_id;;) {
+						size_t lower = m_equivalent_with[last];
+						if (lower == last) {
+							m_equivalent_with[chain_id] = lower;
+							chain_id = lower;
+							break;
+						}
+						last = lower;
+					}
+				}
+				return chain_id;
+			}
+			size_t 			  	merge(size_t chain_id1, size_t chain_id2) {
+				size_t chain_id = std::min((*this)(chain_id1), (*this)(chain_id2));
+				m_equivalent_with[chain_id1] = chain_id;
+				m_equivalent_with[chain_id2] = chain_id;
+				return chain_id;
+			}
+
+#ifndef NDEBUG
+			bool				validate()
+			{
+				assert(m_last_chain_id > 0);
+				assert(m_last_chain_id + 1 == m_equivalent_with.size());
+				for (size_t i = 0; i < m_equivalent_with.size(); ++ i) {
+					for (size_t last = i;;) {
+						size_t lower = m_equivalent_with[last];
+						assert(lower <= last);
+						if (lower == last)
+							break;
+						last = lower;
+					}
+				}
+				return true;
+			}
+#endif /* NDEBUG */
+
+		private:
+			// Unique chain ID assigned to chains of end points of entities.
+			size_t              m_last_chain_id = 0;
+			std::vector<size_t> m_equivalent_with;
+		} equivalent_chain(entities.size());
+
+		// Find the first end point closest to start_near.
+		EndPoint *first_point = nullptr;
+		size_t    first_point_idx = std::numeric_limits<size_t>::max();
+		if (start_near != nullptr) {
+			size_t idx = find_closest_point(kdtree, start_near->cast<double>());
+			assert(idx != kdtree.npos);
+			assert(idx < end_points.size());
+			first_point = &end_points[idx];
+			first_point->distance_out = 0.;
+			first_point->chain_id = equivalent_chain.next();
+			first_point_idx = idx;
+		}
+
+#ifndef NDEBUG
+		auto validate_graph = [&end_points, &equivalent_chain]() -> bool {
+			for (EndPoint& ep : end_points)
+				ep.validate();
+			assert(equivalent_chain.validate());
+			return true;
+		};
+#endif /* NDEBUG */
+
+		// Assign the closest point and distance to the end points.
+		assert(validate_graph());
+		for (EndPoint &end_point : end_points) {
+	    	assert(end_point.edge_out == nullptr);
+	    	if (&end_point != first_point) {
+		    	size_t this_idx = &end_point - &end_points.front();
+		    	// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda).
+		    	// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
+				size_t next_idx = find_closest_point(kdtree, end_point.pos, 
+					[this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; });
+				assert(next_idx != kdtree.npos);
+				assert(next_idx < end_points.size());
+				EndPoint &end_point2 = end_points[next_idx];
+				end_point.edge_out = &end_point2;
+				if (end_point2.edge_in == nullptr)
+					end_point2.edge_in = &end_point;
+				else {
+					assert(end_point.on_circle_empty());
+					assert(end_point2.edge_in->edge_out == &end_point2);
+					end_point.on_circle_merge(end_point2.edge_in);
+				}
+				end_point.distance_out = (end_point2.pos - end_point.pos).squaredNorm();
+			}
+			assert(validate_graph());
+		}
+
+	    // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path.
+	    auto queue = make_mutable_priority_queue<EndPoint*>(
+			[](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, 
+	    	[](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; });
+		queue.reserve(end_points.size() * 2 - 1);
+	    for (EndPoint &ep : end_points)
+	    	if (first_point != &ep)
+				queue.push(&ep);
+
+#ifndef NDEBUG
+		auto validate_graph_and_queue = [&validate_graph, &end_points, &queue, first_point]() -> bool {
+			assert(validate_graph());
+			for (EndPoint &ep : end_points) {
+				if (ep.heap_idx < queue.size()) {
+					// End point is on the heap.
+					assert(*(queue.cbegin() + ep.heap_idx) == &ep);
+					assert(ep.chain_id == 0);
+					// Point on the heap may only points to other points on the heap.
+					assert(ep.edge_in  == nullptr || ep.edge_in ->heap_idx < queue.size());
+					assert(ep.edge_out == nullptr || ep.edge_out->heap_idx < queue.size());
+				} else {
+					// End point is NOT on the heap, therefore it is part of the output path.
+					assert(ep.heap_idx == std::numeric_limits<size_t>::max());
+					assert(ep.chain_id != 0);
+					assert(ep.on_circle_empty());
+					if (&ep == first_point) {
+						assert(ep.edge_in  == nullptr);
+						assert(ep.edge_out == nullptr);
+					} else {
+						assert(ep.edge_in  != nullptr);
+						assert(ep.edge_out != nullptr);
+						assert(ep.edge_in != &ep);
+						assert(ep.edge_in == ep.edge_out);
+						assert(ep.edge_in->edge_out == &ep);
+						assert(ep.edge_out->edge_in == &ep);
+						assert(ep.edge_in->heap_idx == std::numeric_limits<size_t>::max());
+						// Detect loops.
+						for (EndPoint *pt = &ep; pt != nullptr;) {
+							// Out of queue. It is a final point.
+							assert(pt->heap_idx == std::numeric_limits<size_t>::max());
+							EndPoint *pt_other = &end_points[(pt - &end_points.front()) ^ 1];
+							if (pt_other->heap_idx < queue.size())
+								// The other side of this segment is undecided yet.
+								break;
+							pt = pt_other->edge_out;
+						}
+					}
+				}
+			}
+			for (EndPoint *ep : queue)
+				// Points in the queue are not connected yet.
+				assert(ep->chain_id == 0);
+			return true;
+		};
+#endif /* NDEBUG */
+
+	    // Chain the end points: find (entities.size() - 1) shortest links not forming bifurcations or loops.
+	    std::vector<EndPoint*> end_points_update;
+	    end_points_update.reserve(16);
+		assert(entities.size() >= 2);
+		for (int iter = int(entities.size()) - 2;; -- iter) {
+			assert(validate_graph_and_queue());
+	    	// Take the first end point, for which the link points to the currently closest valid neighbor.
+	    	EndPoint &end_point1 = *queue.top();
+	    	assert(end_point1.edge_out != nullptr);
+	    	// No point on the queue may be connected yet.
+	    	assert(end_point1.chain_id == 0);
+	    	// Take the closest end point to the first end point,
+	    	EndPoint &end_point2 = *end_point1.edge_out;
+	    	// The closest point must not be connected yet.
+	    	assert(end_point2.chain_id == 0);
+			// If end_point1.edge_out == end_point2, then end_point2.edge_in == &end_point1, or end_point2.edge_in points to some point on loop of end_point1.
+			assert(end_point2.edge_in != nullptr);
+			// End points of the opposite ends of the segments.
+			size_t end_point1_other_chain_id = equivalent_chain(end_points[(&end_point1 - &end_points.front()) ^ 1].chain_id);
+			size_t end_point2_other_chain_id = equivalent_chain(end_points[(&end_point2 - &end_points.front()) ^ 1].chain_id);
+			if (end_point1_other_chain_id == end_point2_other_chain_id && end_point1_other_chain_id != 0) {
+				// This edge forms a loop. Update end_point1 and try another one.
+				++ iter;
+				assert(end_point1.edge_out != nullptr);
+				assert(end_point1.edge_out->edge_in != nullptr);
+				assert(! end_point1.on_circle_empty() || end_point1.edge_out->edge_in == &end_point1);
+				end_point1.edge_out->edge_in = end_point1.on_circle_empty() ? nullptr : end_point1.on_circle_next;
+				end_point1.edge_out = nullptr;
+				if (! end_point1.on_circle_empty())
+					end_point1.on_circle_detach();
+				assert(validate_graph_and_queue());
+				end_points_update.emplace_back(&end_point1);
+			} else {
+		    	// Remove the first and second point from the queue.
+				queue.pop();
+		    	queue.remove(end_point2.heap_idx);
+	#ifndef NDEBUG
+				// Mark them as removed from the queue.
+				end_point1.heap_idx = std::numeric_limits<size_t>::max();
+				end_point2.heap_idx = std::numeric_limits<size_t>::max();
+	#endif /* NDEBUG */
+				// Collect the other end points pointing to this one, detach them from the on_circle linked list.
+				for (EndPoint *pt_first : { end_point1.edge_in, end_point2.edge_in })
+					if (pt_first != nullptr) {
+						EndPoint *pt = pt_first;
+						do {
+							if (pt != &end_point1 && pt != &end_point2) {
+								// Point is in the queue.
+								assert(pt->heap_idx < queue.size());
+								// Point is not connected yet.
+								assert(pt->chain_id == 0);
+								end_points_update.emplace_back(pt);
+								pt->edge_out = nullptr;
+							}
+							EndPoint *next = pt->on_circle_next;
+							pt->on_circle_prev = nullptr;
+							pt->on_circle_next = nullptr;
+							pt = next;
+						} while (pt != nullptr && pt != pt_first);
+					}
+				// If end_point1 was on a circle, the circle belonged to end_point2.edge_in, which was broken in the loop above.
+				assert(end_point1.on_circle_empty());
+				// If end_point2 pointed to end_point1, then end_point2 was on a circle that belonged to end_point1.edge_in, which was broken in the loop above.
+				//assert(end_point2.on_circle_empty() == (end_point2.edge_out == &end_point1));
+				assert(end_point2.on_circle_empty() || end_point2.edge_out != nullptr);
+				end_point2.edge_out->edge_in = end_point2.on_circle_empty() ? nullptr : end_point2.on_circle_next;
+		    	// The end_point2.link may not necessarily point back to end_point1 due to numeric issues and points on circles.
+		    	// Update the link back.
+		    	end_point1.edge_out = &end_point2;
+		    	end_point1.edge_in  = &end_point2;
+		    	end_point2.edge_out = &end_point1;
+		    	end_point2.edge_in  = &end_point1;
+		    	end_point2.distance_out = end_point1.distance_out;
+		    	// Assign chain IDs to the newly connected end points, set equivalent_chain if two chains were merged.
+		    	size_t chain_id =
+					(end_point1_other_chain_id == 0) ?
+						((end_point2_other_chain_id == 0) ? equivalent_chain.next() : end_point2_other_chain_id) :
+						((end_point2_other_chain_id == 0) ? end_point1_other_chain_id : 
+							(end_point1_other_chain_id == end_point2_other_chain_id) ? 
+								end_point1_other_chain_id :
+								equivalent_chain.merge(end_point1_other_chain_id, end_point2_other_chain_id));
+				end_point1.chain_id = chain_id;
+				end_point2.chain_id = chain_id;
+				if (! end_point2.on_circle_empty())
+					end_point2.on_circle_detach();
+				assert(validate_graph_and_queue());
+			}
+#ifndef NDEBUG
+			for (EndPoint *end_point : end_points_update) {
+				assert(end_point->edge_out == nullptr);
+				// Point is in the queue.
+				assert(end_point->heap_idx < queue.size());
+				// Point is not connected yet.
+				assert(end_point->chain_id == 0);
+			}
+#endif /* NDEBUG */
+			if (iter == 0) {
+				// Last iteration. There shall be exactly one or two end points waiting to be connected.
+				if (first_point == nullptr) {
+					// Two unconnected points are the end points of the constructed path.
+					assert(end_points_update.size() == 2);
+					first_point = end_points_update.front();
+				} else
+					assert(end_points_update.size() == 1);
+				// Mark both points as ends of the path.
+				for (EndPoint *end_point : end_points_update)
+					end_point->edge_in = end_point->edge_out = nullptr;
+				break;
+			}
+	    	// Update links, distances and queue positions of all points that used to point to end_point1 or end_point2.
+	    	for (EndPoint *end_point : end_points_update) {
+		    	size_t this_idx = end_point - &end_points.front();
+		    	// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda).
+				size_t next_idx = find_closest_point(kdtree, end_point->pos, [&end_points, &equivalent_chain, this_idx](size_t idx) { 
+			    	assert(end_points[this_idx].edge_out == nullptr);
+			    	assert(end_points[this_idx].chain_id == 0);
+					if ((idx ^ this_idx) <= 1 || end_points[idx].chain_id != 0)
+						// Points of the same segment shall not be connected,
+						// cannot connect to an already connected point (ideally those would be removed from the KD tree, but the update is difficult).
+						return false;
+			    	size_t chain1 = equivalent_chain(end_points[this_idx ^ 1].chain_id);
+			    	size_t chain2 = equivalent_chain(end_points[idx      ^ 1].chain_id);
+			    	return chain1 != chain2 || chain1 == 0;
+				});
+				assert(next_idx != kdtree.npos);
+				assert(next_idx < end_points.size());
+				EndPoint &end_point2 = end_points[next_idx];
+				end_point->edge_out = &end_point2;
+				if (end_point2.edge_in == nullptr)
+					end_point2.edge_in = end_point;
+				else {
+					assert(end_point->on_circle_empty());
+					assert(end_point2.edge_in->edge_out == &end_point2);
+					end_point->on_circle_merge(end_point2.edge_in);
+				}
+				end_point->distance_out = (end_points[next_idx].pos - end_point->pos).squaredNorm();
+				// Update position of this end point in the queue based on the distance calculated at the line above.
+				queue.update(end_point->heap_idx);
+		    	//FIXME Remove the other end point from the KD tree.
+		    	// As the KD tree update is expensive, do it only after some larger number of points is removed from the queue.
+				assert(validate_graph_and_queue());
+	    	}
+			end_points_update.clear();
+		}
+		assert(queue.size() == (first_point == nullptr) ? 1 : 2);
+
+		// Now interconnect pairs of segments into a chain.
+		assert(first_point != nullptr);
+		do {
+			size_t    		 first_point_id      = first_point - &end_points.front();
+			size_t           extrusion_entity_id = first_point_id >> 1;
+			EndPoint 		*second_point        = &end_points[first_point_id ^ 1];
+			ExtrusionEntity *extrusion_entity    = entities[extrusion_entity_id];
+			out.emplace_back(extrusion_entity_id, extrusion_entity->can_reverse() && (first_point_id & 1));
+			first_point = second_point->edge_out;
+		} while (first_point != nullptr);
+	}
+
+	assert(out.size() == entities.size());
+	return out;
+}
+
+} // namespace Slic3r
diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
new file mode 100644
index 000000000..2a7e67688
--- /dev/null
+++ b/src/libslic3r/ShortestPath.hpp
@@ -0,0 +1,17 @@
+#ifndef slic3r_ShortestPath_hpp_
+#define slic3r_ShortestPath_hpp_
+
+#include "libslic3r.h"
+#include "ExtrusionEntity.hpp"
+#include "Point.hpp"
+
+#include <utility>
+#include <vector>
+
+namespace Slic3r {
+
+std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
+
+} // namespace Slic3r
+
+#endif /* slic3r_ShortestPath_hpp_ */

From 10eecb2cab8ab953a6b884589a56094c7ebc7a96 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 26 Sep 2019 16:39:50 +0200
Subject: [PATCH 32/44] Reworked Traveling Salesman Problem code for simplicity
 and robustness. The TSP algorithm is newly used for planning of the printing
 order of objects AND their instances.

---
 src/libslic3r/ExtrusionEntity.hpp      |   2 +
 src/libslic3r/GCode.cpp                | 146 ++++++----
 src/libslic3r/GCode.hpp                |  25 +-
 src/libslic3r/MutablePriorityQueue.hpp |  21 +-
 src/libslic3r/Print.cpp                |  16 +-
 src/libslic3r/Print.hpp                |   1 +
 src/libslic3r/ShortestPath.cpp         | 388 +++++++++----------------
 src/libslic3r/ShortestPath.hpp         |  10 +
 8 files changed, 293 insertions(+), 316 deletions(-)

diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index ce52ae152..dfc180689 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -5,6 +5,8 @@
 #include "Polygon.hpp"
 #include "Polyline.hpp"
 
+#include <assert.h>
+
 namespace Slic3r {
 
 class ExPolygonCollection;
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 0ccc3ddf5..8bd42e59a 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -6,6 +6,7 @@
 #include "Geometry.hpp"
 #include "GCode/PrintExtents.hpp"
 #include "GCode/WipeTower.hpp"
+#include "ShortestPath.hpp"
 #include "Utils.hpp"
 
 #include <algorithm>
@@ -1160,7 +1161,7 @@ void GCode::_do_export(Print &print, FILE *file)
                 for (const LayerToPrint &ltp : layers_to_print) {
                     std::vector<LayerToPrint> lrs;
                     lrs.emplace_back(std::move(ltp));
-                    this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object.copies().data());
+                    this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, &copy - object.copies().data());
                     print.throw_if_canceled();
                 }
 #ifdef HAS_PRESSURE_EQUALIZER
@@ -1174,12 +1175,8 @@ void GCode::_do_export(Print &print, FILE *file)
             }
         }
     } else {
-        // Order objects using a nearest neighbor search.
-        std::vector<size_t> object_indices;
-        Points object_reference_points;
-        for (PrintObject *object : print.objects())
-            object_reference_points.push_back(object->copies().front());
-        Slic3r::Geometry::chained_path(object_reference_points, object_indices);
+        // Order object instances using a nearest neighbor search.
+        std::vector<std::pair<size_t, size_t>> print_object_instances_ordering = chain_print_object_instances(print);
         // Sort layers by Z.
         // All extrusion moves with the same top layer height are extruded uninterrupted.
         std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
@@ -1218,7 +1215,7 @@ void GCode::_do_export(Print &print, FILE *file)
             const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
             if (m_wipe_tower && layer_tools.has_wipe_tower)
                 m_wipe_tower->next_layer();
-            this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
+            this->process_layer(file, print, layer.second, layer_tools, &print_object_instances_ordering, size_t(-1));
             print.throw_if_canceled();
         }
 #ifdef HAS_PRESSURE_EQUALIZER
@@ -1529,8 +1526,54 @@ inline std::vector<GCode::ObjectByExtruder::Island>& object_islands_by_extruder(
     return islands;
 }
 
+std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
+	std::vector<GCode::ObjectByExtruder> 			&objects_by_extruder,
+	const std::vector<LayerToPrint> 				&layers,
+	// Ordering must be defined for normal (non-sequential print).
+	const std::vector<std::pair<size_t, size_t>> 	*ordering,
+	// For sequential print, the instance of the object to be printing has to be defined.
+	const size_t                     				 single_object_instance_idx)
+{
+    std::vector<InstanceToPrint> out;
+
+    if (ordering == nullptr) {
+    	// Sequential print, single object is being printed.
+		for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
+		    const size_t       layer_id     = &object_by_extruder - objects_by_extruder.data();
+		    const PrintObject *print_object = layers[layer_id].object();
+		    if (print_object)
+		    	out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx);
+		}
+    } else {
+		// Create mapping from PrintObject* to ObjectByExtruder*.
+		std::vector<std::pair<const PrintObject*, ObjectByExtruder*>> sorted;
+		sorted.reserve(objects_by_extruder.size());
+		for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
+		    const size_t       layer_id     = &object_by_extruder - objects_by_extruder.data();
+		    const PrintObject *print_object = layers[layer_id].object();
+		    if (print_object)
+		    	sorted.emplace_back(print_object, &object_by_extruder);
+		}
+		std::sort(sorted.begin(), sorted.end());
+
+		if (! sorted.empty()) {
+			const Print &print = *sorted.front().first->print();
+		    out.reserve(sorted.size());
+		    for (const std::pair<size_t, size_t> &instance_id : *ordering) {
+		    	const PrintObject &print_object = *print.objects()[instance_id.first];
+		    	std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
+		    	auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
+		    	if (it != sorted.end() && it->first == &print_object)
+		    		// ObjectByExtruder for this PrintObject was found.
+					out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second);
+		    }
+		}
+	}
+	return out;
+}
+
 // In sequential mode, process_layer is called once per each object and its copy, 
-// therefore layers will contain a single entry and single_object_idx will point to the copy of the object.
+// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object.
 // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
 // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths
 // and performing the extruder specific extrusions together.
@@ -1541,14 +1584,16 @@ void GCode::process_layer(
     // Set of object & print layers of the same PrintObject and with the same print_z.
     const std::vector<LayerToPrint> &layers,
     const LayerTools                &layer_tools,
+	// Pairs of PrintObject index and its instance index.
+	const std::vector<std::pair<size_t, size_t>> *ordering,
     // If set to size_t(-1), then print all copies of all objects.
     // Otherwise print a single copy of a single object.
-    const size_t                     single_object_idx)
+    const size_t                     single_object_instance_idx)
 {
     assert(! layers.empty());
 //    assert(! layer_tools.extruders.empty());
     // Either printing all copies of all objects, or just a single copy of a single object.
-    assert(single_object_idx == size_t(-1) || layers.size() == 1);
+    assert(single_object_instance_idx == size_t(-1) || layers.size() == 1);
 
     if (layer_tools.extruders.empty())
         // Nothing to extrude.
@@ -1883,62 +1928,49 @@ void GCode::process_layer(
         if (objects_by_extruder_it == by_extruder.end())
             continue;
 
+		std::vector<InstanceToPrint> instances_to_print = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx);
+
         // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
         bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
         for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
             if (is_anything_overridden && print_wipe_extrusions == 0)
                 gcode+="; PURGING FINISHED\n";
 
-            for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
-                const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
-                const PrintObject *print_object = layers[layer_id].object();
-                if (print_object == nullptr)
-                    // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
-                    continue;
-
-                m_config.apply(print_object->config(), true);
-                m_layer = layers[layer_id].layer();
+            for (InstanceToPrint &instance_to_print : instances_to_print) {
+                m_config.apply(instance_to_print.print_object.config(), true);
+                m_layer = layers[instance_to_print.layer_id].layer();
                 if (m_config.avoid_crossing_perimeters)
                     m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
-                Points copies;
-                if (single_object_idx == size_t(-1))
-                    copies = print_object->copies();
-                else
-                    copies.push_back(print_object->copies()[single_object_idx]);
-                // Sort the copies by the closest point starting with the current print position.
 
-                unsigned int copy_id = 0;
-                for (const Point &copy : copies) {
-                    if (this->config().gcode_label_objects)
-                        gcode += std::string("; printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n";
-                    // When starting a new object, use the external motion planner for the first travel move.
-                    std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
-                    if (m_last_obj_copy != this_object_copy)
-                        m_avoid_crossing_perimeters.use_external_mp_once = true;
-                    m_last_obj_copy = this_object_copy;
-                    this->set_origin(unscale(copy));
-                    if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
-                        m_layer = layers[layer_id].support_layer;
-                        gcode += this->extrude_support(
-                            // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
-                            object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
-                        m_layer = layers[layer_id].layer();
-                    }
-                    for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
-                        const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
-
-                        if (print.config().infill_first) {
-                            gcode += this->extrude_infill(print, by_region_specific);
-                            gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
-                        } else {
-                            gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
-                            gcode += this->extrude_infill(print,by_region_specific);
-                        }
-                    }
-                    if (this->config().gcode_label_objects)
-						gcode += std::string("; stop printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n";
-                    ++ copy_id;
+                if (this->config().gcode_label_objects)
+                    gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
+                // When starting a new object, use the external motion planner for the first travel move.
+                const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id];
+                std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
+                if (m_last_obj_copy != this_object_copy)
+                    m_avoid_crossing_perimeters.use_external_mp_once = true;
+                m_last_obj_copy = this_object_copy;
+                this->set_origin(unscale(offset));
+                if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
+                    m_layer = layers[instance_to_print.layer_id].support_layer;
+                    gcode += this->extrude_support(
+                        // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
+                        instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role));
+                    m_layer = layers[instance_to_print.layer_id].layer();
                 }
+                for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
+                    const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region;
+
+                    if (print.config().infill_first) {
+                        gcode += this->extrude_infill(print, by_region_specific);
+                        gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
+                    } else {
+                        gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
+                        gcode += this->extrude_infill(print,by_region_specific);
+                    }
+                }
+                if (this->config().gcode_label_objects)
+					gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
             }
         }
     }
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 72813810b..45ff7eda6 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -202,7 +202,7 @@ protected:
         const PrintObject*    object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
         coordf_t              print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
     };
-    static std::vector<GCode::LayerToPrint>                            collect_layers_to_print(const PrintObject &object);
+    static std::vector<LayerToPrint>        		                   collect_layers_to_print(const PrintObject &object);
     static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
     void            process_layer(
         // Write into the output file.
@@ -210,7 +210,9 @@ protected:
         const Print                     &print,
         // Set of object & print layers of the same PrintObject and with the same print_z.
         const std::vector<LayerToPrint> &layers,
-        const LayerTools  &layer_tools,
+        const LayerTools  				&layer_tools,
+		// Pairs of PrintObject index and its instance index.
+		const std::vector<std::pair<size_t, size_t>> *ordering,
         // If set to size_t(-1), then print all copies of all objects.
         // Otherwise print a single copy of a single object.
         const size_t                     single_object_idx = size_t(-1));
@@ -258,6 +260,25 @@ protected:
         std::vector<Island>         islands;
     };
 
+	struct InstanceToPrint
+	{
+		InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) :
+			object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {}
+
+		ObjectByExtruder	&object_by_extruder;
+		const size_t       		 layer_id;
+		const PrintObject 		&print_object;
+		// Instance idx of the copy of a print object.
+		const size_t			 instance_id;
+	};
+
+	std::vector<InstanceToPrint> sort_print_object_instances(
+		std::vector<ObjectByExtruder> 			&objects_by_extruder,
+		const std::vector<LayerToPrint> 				&layers,
+		// Ordering must be defined for normal (non-sequential print).
+		const std::vector<std::pair<size_t, size_t>> 	*ordering,
+		// For sequential print, the instance of the object to be printing has to be defined.
+		const size_t                     				 single_object_instance_idx);
 
     std::string     extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
     std::string     extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
diff --git a/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp
index 31946c707..da469b7ba 100644
--- a/src/libslic3r/MutablePriorityQueue.hpp
+++ b/src/libslic3r/MutablePriorityQueue.hpp
@@ -13,7 +13,7 @@ public:
 		{}
 	~MutablePriorityQueue()	{ clear(); }
 
-	void		clear()								{ m_heap.clear(); }
+	void		clear();
 	void		reserve(size_t cnt) 				{ m_heap.reserve(cnt); }
 	void		push(const T &item);
 	void		push(T &&item);
@@ -49,6 +49,17 @@ MutablePriorityQueue<T, IndexSetter, LessPredicate> make_mutable_priority_queue(
     	std::forward<IndexSetter>(index_setter), std::forward<LessPredicate>(less_predicate));
 }
 
+template<class T, class LessPredicate, class IndexSetter>
+inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::clear()
+{ 
+#ifndef NDEBUG
+	for (size_t idx = 0; idx < m_heap.size(); ++ idx)
+		// Mark as removed from the queue.
+		m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
+#endif /* NDEBUG */
+	m_heap.clear();
+}
+
 template<class T, class LessPredicate, class IndexSetter>
 inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::push(const T &item)
 {
@@ -71,6 +82,10 @@ template<class T, class LessPredicate, class IndexSetter>
 inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::pop()
 {
 	assert(! m_heap.empty());
+#ifndef NDEBUG
+	// Mark as removed from the queue.
+	m_index_setter(m_heap.front(), std::numeric_limits<size_t>::max());
+#endif /* NDEBUG */
 	if (m_heap.size() > 1) {
 		m_heap.front() = m_heap.back();
 		m_heap.pop_back();
@@ -84,6 +99,10 @@ template<class T, class LessPredicate, class IndexSetter>
 inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::remove(size_t idx)
 {
 	assert(idx < m_heap.size());
+#ifndef NDEBUG
+	// Mark as removed from the queue.
+	m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
+#endif /* NDEBUG */
 	if (idx + 1 == m_heap.size()) {
 		m_heap.pop_back();
 		return;
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 4d8482743..347cfa2ce 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -7,6 +7,7 @@
 #include "Flow.hpp"
 #include "Geometry.hpp"
 #include "I18N.hpp"
+#include "ShortestPath.hpp"
 #include "SupportMaterial.hpp"
 #include "GCode.hpp"
 #include "GCode/WipeTower.hpp"
@@ -1824,8 +1825,8 @@ void Print::_make_brim()
 				[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
 					return l.second < r.second;
 				});
-			Vec3f last_pt(0.f, 0.f, 0.f);
 
+			Point last_pt(0, 0);
 			for (size_t i = 0; i < loops_trimmed_order.size();) {
 				// Find all pieces that the initial loop was split into.
 				size_t j = i + 1;
@@ -1841,16 +1842,23 @@ void Print::_make_brim()
 		            	points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
 		            i = j;
 				} else {
-			    	//FIXME this is not optimal as the G-code generator will follow the sequence of paths verbatim without respect to minimum travel distance.
+			    	//FIXME The path chaining here may not be optimal.
+			    	ExtrusionEntityCollection this_loop_trimmed;
+					this_loop_trimmed.entities.reserve(j - i);
 			    	for (; i < j; ++ i) {
-			            m_brim.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())));
+			            this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())));
 						const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
-			            Points &points = static_cast<ExtrusionPath*>(m_brim.entities.back())->polyline.points;
+			            Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
 			            points.reserve(path.size());
 			            for (const ClipperLib_Z::IntPoint &pt : path)
 			            	points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
 		           	}
+		           	chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
+		           	m_brim.entities.reserve(m_brim.entities.size() + this_loop_trimmed.entities.size());
+		           	append(m_brim.entities, std::move(this_loop_trimmed.entities));
+		           	this_loop_trimmed.entities.clear();
 		        }
+		        last_pt = m_brim.last_point();
 			}
 		}
     } else {
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 89a5f3e74..5cb13039c 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -96,6 +96,7 @@ public:
     const SupportLayerPtrs& support_layers() const  { return m_support_layers; }
     const Transform3d&      trafo() const           { return m_trafo; }
     const Points&           copies() const          { return m_copies; }
+    const Point 			copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
 
     // since the object is aligned to origin, bounding box coincides with size
     BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index e98b0a58c..79e0fdf11 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -1,12 +1,14 @@
-#include "ShortestPath.hpp"
-#include "KDTreeIndirect.hpp"
-#include "MutablePriorityQueue.hpp"
-
 #if 0
+	#pragma optimize("", off)
 	#undef NDEBUG
 	#undef assert
 #endif
 
+#include "ShortestPath.hpp"
+#include "KDTreeIndirect.hpp"
+#include "MutablePriorityQueue.hpp"
+#include "Print.hpp"
+
 #include <cmath>
 #include <cassert>
 
@@ -20,135 +22,44 @@ namespace Slic3r {
 // The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which 
 // is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates 
 // a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities.
-std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
+template<typename PointType, typename SegmentEndPointFunc>
+std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
 {
 	std::vector<std::pair<size_t, bool>> out;
 
-	if (entities.empty()) {
+	if (num_segments == 0) {
 		// Nothing to do.
 	} 
-	else if (entities.size() == 1)
+	else if (num_segments == 1)
 	{
 		// Just sort the end points so that the first point visited is closest to start_near.
-		ExtrusionEntity *extrusion_entity = entities.front();
-		out.emplace_back(0, extrusion_entity->can_reverse() && start_near != nullptr && 
-			(extrusion_entity->last_point() - *start_near).cast<double>().squaredNorm() < (extrusion_entity->first_point() - *start_near).cast<double>().squaredNorm());
+		out.emplace_back(0, start_near != nullptr && 
+			(end_point_func(0, true) - *start_near).cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).cast<double>().squaredNorm());
 	} 
 	else
 	{
-		// End points of entities for the KD tree closest point search.
+		// End points of segments for the KD tree closest point search.
 		// A single end point is inserted into the search structure for loops, two end points are entered for open paths.
 		struct EndPoint {
 			EndPoint(const Vec2d &pos) : pos(pos) {}
-
 			Vec2d     pos;
-
 			// Identifier of the chain, to which this end point belongs. Zero means unassigned.
 			size_t    chain_id = 0;
 			// Link to the closest currently valid end point.
 			EndPoint *edge_out = nullptr;
-			// Reverse of edge_out. As there may be multiple end points with the same edge_out,
-			// these other edge_in points are chained using the on_circle_prev / on_circle_next cyclic loop.
-			EndPoint *edge_in  = nullptr;
-			EndPoint* on_circle_prev = nullptr;
-			EndPoint* on_circle_next = nullptr;
-			void 	  on_circle_merge(EndPoint *other)
-			{
-				EndPoint *a = this;
-				EndPoint *b = other;
-				assert(a->validate());
-				assert(b->validate());
-				if (a->on_circle_next == nullptr)
-					std::swap(a, b);
-				if (a->on_circle_next == nullptr) {
-					a->on_circle_next = a->on_circle_prev = b;
-					b->on_circle_next = b->on_circle_prev = a;
-				} else if (b->on_circle_next == nullptr) {
-					b->on_circle_next = a;
-					b->on_circle_prev = a->on_circle_prev;
-					a->on_circle_prev = b;
-					b->on_circle_prev->on_circle_next = b;
-				} else {
-					EndPoint *next = a->on_circle_next;
-					EndPoint *prev = b->on_circle_prev;
-					a->on_circle_next = b;
-					b->on_circle_prev = a;
-					prev->on_circle_next = next;
-					next->on_circle_prev = prev;
-				}
-				assert(this->validate());
-			}
-			void 	  on_circle_detach() 
-			{
-				if (this->on_circle_next) {
-					EndPoint *next = this->on_circle_next;
-					EndPoint *prev = this->on_circle_prev;
-					if (prev == next) {
-						next->on_circle_next = nullptr;
-						next->on_circle_prev = nullptr;
-					} else {
-						prev->on_circle_next = next;
-						next->on_circle_prev = prev;
-					}
-					assert(prev->validate());
-					assert(next->validate());
-					this->on_circle_next = this->on_circle_prev = nullptr;
-				}
-				assert(this->validate());
-			}
-			bool 	  on_circle_empty() const
-			{
-				assert((this->on_circle_prev == nullptr) == (this->on_circle_next == nullptr));
-				assert(this->on_circle_prev == nullptr || (this->on_circle_prev != this && this->on_circle_next != this));
-				return this->on_circle_next == nullptr;
-			}
-
-#ifndef NDEBUG
-			bool	  validate() 
-			{
-				assert((this->on_circle_prev == nullptr) == (this->on_circle_next == nullptr));
-				assert(this->on_circle_prev == nullptr || (this->on_circle_prev != this && this->on_circle_next != this));
-				assert(this->edge_out == nullptr || edge_out->edge_in != nullptr);
-				assert(this->distance_out >= 0.);
-				assert(this->edge_in == nullptr || this->edge_in->edge_out == this);
-				// Point which is a member of path (chain_id > 0) must not be in circle of some edge_in.
-				assert(this->chain_id == 0 || this->on_circle_empty());
-				if (! this->on_circle_empty()) {
-					// Iterate over the cycle and validate the loop.
-					std::set<const EndPoint*> visited;
-					const EndPoint *ep = this;
-					bool edge_in_found = false;
-					do {
-						// This end point is visited for the first time.
-						assert(visited.insert(ep).second);
-						assert(ep->on_circle_next != ep);
-						assert(ep->on_circle_prev != ep);
-						assert(ep->on_circle_next->on_circle_prev == ep);
-						assert(ep->on_circle_prev->on_circle_next == ep);
-						assert(ep->edge_out != nullptr && ep->edge_out == this->edge_out);
-						if (ep->edge_out->edge_in == ep)
-							edge_in_found = true;
-						ep = ep->on_circle_next;
-					} while (ep != this);
-					assert(edge_in_found);
-				}
-				return true;
-			}
-#endif /* NDEBUG */
-
 			// Distance to the next end point following the link.
 			// Zero value -> start of the final path.
 			double    distance_out = std::numeric_limits<double>::max();
 			size_t    heap_idx = std::numeric_limits<size_t>::max();
 		};
 	    std::vector<EndPoint> end_points;
-	    end_points.reserve(entities.size() * 2);
-	    for (const ExtrusionEntity* const &entity : entities) {
-	    	end_points.emplace_back(entity->first_point().cast<double>());
-	    	end_points.emplace_back(entity->last_point().cast<double>());
+	    end_points.reserve(num_segments * 2);
+	    for (size_t i = 0; i < num_segments; ++ i) {
+	    	end_points.emplace_back(end_point_func(i, true ).cast<double>());
+	    	end_points.emplace_back(end_point_func(i, false).cast<double>());
 	    }
 
-	    // Construct the closest point KD tree over end points of extrusion entities.
+	    // Construct the closest point KD tree over end points of segments.
 		auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; };
 		KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size());
 
@@ -189,7 +100,7 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 #ifndef NDEBUG
 			bool				validate()
 			{
-				assert(m_last_chain_id > 0);
+				assert(m_last_chain_id >= 0);
 				assert(m_last_chain_id + 1 == m_equivalent_with.size());
 				for (size_t i = 0; i < m_equivalent_with.size(); ++ i) {
 					for (size_t last = i;;) {
@@ -205,17 +116,16 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 #endif /* NDEBUG */
 
 		private:
-			// Unique chain ID assigned to chains of end points of entities.
+			// Unique chain ID assigned to chains of end points of segments.
 			size_t              m_last_chain_id = 0;
 			std::vector<size_t> m_equivalent_with;
-		} equivalent_chain(entities.size());
+		} equivalent_chain(num_segments);
 
 		// Find the first end point closest to start_near.
 		EndPoint *first_point = nullptr;
 		size_t    first_point_idx = std::numeric_limits<size_t>::max();
 		if (start_near != nullptr) {
 			size_t idx = find_closest_point(kdtree, start_near->cast<double>());
-			assert(idx != kdtree.npos);
 			assert(idx < end_points.size());
 			first_point = &end_points[idx];
 			first_point->distance_out = 0.;
@@ -223,17 +133,7 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 			first_point_idx = idx;
 		}
 
-#ifndef NDEBUG
-		auto validate_graph = [&end_points, &equivalent_chain]() -> bool {
-			for (EndPoint& ep : end_points)
-				ep.validate();
-			assert(equivalent_chain.validate());
-			return true;
-		};
-#endif /* NDEBUG */
-
 		// Assign the closest point and distance to the end points.
-		assert(validate_graph());
 		for (EndPoint &end_point : end_points) {
 	    	assert(end_point.edge_out == nullptr);
 	    	if (&end_point != first_point) {
@@ -242,20 +142,11 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 		    	// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
 				size_t next_idx = find_closest_point(kdtree, end_point.pos, 
 					[this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; });
-				assert(next_idx != kdtree.npos);
 				assert(next_idx < end_points.size());
 				EndPoint &end_point2 = end_points[next_idx];
 				end_point.edge_out = &end_point2;
-				if (end_point2.edge_in == nullptr)
-					end_point2.edge_in = &end_point;
-				else {
-					assert(end_point.on_circle_empty());
-					assert(end_point2.edge_in->edge_out == &end_point2);
-					end_point.on_circle_merge(end_point2.edge_in);
-				}
 				end_point.distance_out = (end_point2.pos - end_point.pos).squaredNorm();
 			}
-			assert(validate_graph());
 		}
 
 	    // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path.
@@ -268,32 +159,21 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 				queue.push(&ep);
 
 #ifndef NDEBUG
-		auto validate_graph_and_queue = [&validate_graph, &end_points, &queue, first_point]() -> bool {
-			assert(validate_graph());
+		auto validate_graph_and_queue = [&equivalent_chain, &end_points, &queue, first_point]() -> bool {
+			assert(equivalent_chain.validate());
 			for (EndPoint &ep : end_points) {
 				if (ep.heap_idx < queue.size()) {
 					// End point is on the heap.
 					assert(*(queue.cbegin() + ep.heap_idx) == &ep);
 					assert(ep.chain_id == 0);
-					// Point on the heap may only points to other points on the heap.
-					assert(ep.edge_in  == nullptr || ep.edge_in ->heap_idx < queue.size());
-					assert(ep.edge_out == nullptr || ep.edge_out->heap_idx < queue.size());
 				} else {
 					// End point is NOT on the heap, therefore it is part of the output path.
 					assert(ep.heap_idx == std::numeric_limits<size_t>::max());
 					assert(ep.chain_id != 0);
-					assert(ep.on_circle_empty());
 					if (&ep == first_point) {
-						assert(ep.edge_in  == nullptr);
 						assert(ep.edge_out == nullptr);
 					} else {
-						assert(ep.edge_in  != nullptr);
 						assert(ep.edge_out != nullptr);
-						assert(ep.edge_in != &ep);
-						assert(ep.edge_in == ep.edge_out);
-						assert(ep.edge_in->edge_out == &ep);
-						assert(ep.edge_out->edge_in == &ep);
-						assert(ep.edge_in->heap_idx == std::numeric_limits<size_t>::max());
 						// Detect loops.
 						for (EndPoint *pt = &ep; pt != nullptr;) {
 							// Out of queue. It is a final point.
@@ -314,11 +194,9 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 		};
 #endif /* NDEBUG */
 
-	    // Chain the end points: find (entities.size() - 1) shortest links not forming bifurcations or loops.
-	    std::vector<EndPoint*> end_points_update;
-	    end_points_update.reserve(16);
-		assert(entities.size() >= 2);
-		for (int iter = int(entities.size()) - 2;; -- iter) {
+	    // Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops.
+		assert(num_segments >= 2);
+		for (int iter = int(num_segments) - 2;; -- iter) {
 			assert(validate_graph_and_queue());
 	    	// Take the first end point, for which the link points to the currently closest valid neighbor.
 	    	EndPoint &end_point1 = *queue.top();
@@ -327,65 +205,26 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 	    	assert(end_point1.chain_id == 0);
 	    	// Take the closest end point to the first end point,
 	    	EndPoint &end_point2 = *end_point1.edge_out;
-	    	// The closest point must not be connected yet.
-	    	assert(end_point2.chain_id == 0);
-			// If end_point1.edge_out == end_point2, then end_point2.edge_in == &end_point1, or end_point2.edge_in points to some point on loop of end_point1.
-			assert(end_point2.edge_in != nullptr);
-			// End points of the opposite ends of the segments.
-			size_t end_point1_other_chain_id = equivalent_chain(end_points[(&end_point1 - &end_points.front()) ^ 1].chain_id);
-			size_t end_point2_other_chain_id = equivalent_chain(end_points[(&end_point2 - &end_points.front()) ^ 1].chain_id);
-			if (end_point1_other_chain_id == end_point2_other_chain_id && end_point1_other_chain_id != 0) {
-				// This edge forms a loop. Update end_point1 and try another one.
-				++ iter;
-				assert(end_point1.edge_out != nullptr);
-				assert(end_point1.edge_out->edge_in != nullptr);
-				assert(! end_point1.on_circle_empty() || end_point1.edge_out->edge_in == &end_point1);
-				end_point1.edge_out->edge_in = end_point1.on_circle_empty() ? nullptr : end_point1.on_circle_next;
-				end_point1.edge_out = nullptr;
-				if (! end_point1.on_circle_empty())
-					end_point1.on_circle_detach();
-				assert(validate_graph_and_queue());
-				end_points_update.emplace_back(&end_point1);
-			} else {
+	    	bool valid = true;
+	    	size_t end_point1_other_chain_id = 0;
+	    	size_t end_point2_other_chain_id = 0;
+	    	if (end_point2.chain_id > 0) {
+	    		// The other side is part of the output path. Don't connect to end_point2, update end_point1 and try another one.
+	    		valid = false;
+	    	} else {
+				// End points of the opposite ends of the segments.
+				end_point1_other_chain_id = equivalent_chain(end_points[(&end_point1 - &end_points.front()) ^ 1].chain_id);
+				end_point2_other_chain_id = equivalent_chain(end_points[(&end_point2 - &end_points.front()) ^ 1].chain_id);
+				if (end_point1_other_chain_id == end_point2_other_chain_id && end_point1_other_chain_id != 0)
+					// This edge forms a loop. Update end_point1 and try another one.
+					valid = false;
+	    	}
+	    	if (valid) {
 		    	// Remove the first and second point from the queue.
 				queue.pop();
 		    	queue.remove(end_point2.heap_idx);
-	#ifndef NDEBUG
-				// Mark them as removed from the queue.
-				end_point1.heap_idx = std::numeric_limits<size_t>::max();
-				end_point2.heap_idx = std::numeric_limits<size_t>::max();
-	#endif /* NDEBUG */
-				// Collect the other end points pointing to this one, detach them from the on_circle linked list.
-				for (EndPoint *pt_first : { end_point1.edge_in, end_point2.edge_in })
-					if (pt_first != nullptr) {
-						EndPoint *pt = pt_first;
-						do {
-							if (pt != &end_point1 && pt != &end_point2) {
-								// Point is in the queue.
-								assert(pt->heap_idx < queue.size());
-								// Point is not connected yet.
-								assert(pt->chain_id == 0);
-								end_points_update.emplace_back(pt);
-								pt->edge_out = nullptr;
-							}
-							EndPoint *next = pt->on_circle_next;
-							pt->on_circle_prev = nullptr;
-							pt->on_circle_next = nullptr;
-							pt = next;
-						} while (pt != nullptr && pt != pt_first);
-					}
-				// If end_point1 was on a circle, the circle belonged to end_point2.edge_in, which was broken in the loop above.
-				assert(end_point1.on_circle_empty());
-				// If end_point2 pointed to end_point1, then end_point2 was on a circle that belonged to end_point1.edge_in, which was broken in the loop above.
-				//assert(end_point2.on_circle_empty() == (end_point2.edge_out == &end_point1));
-				assert(end_point2.on_circle_empty() || end_point2.edge_out != nullptr);
-				end_point2.edge_out->edge_in = end_point2.on_circle_empty() ? nullptr : end_point2.on_circle_next;
-		    	// The end_point2.link may not necessarily point back to end_point1 due to numeric issues and points on circles.
-		    	// Update the link back.
-		    	end_point1.edge_out = &end_point2;
-		    	end_point1.edge_in  = &end_point2;
+		    	assert(end_point1.edge_out = &end_point2);
 		    	end_point2.edge_out = &end_point1;
-		    	end_point2.edge_in  = &end_point1;
 		    	end_point2.distance_out = end_point1.distance_out;
 		    	// Assign chain IDs to the newly connected end points, set equivalent_chain if two chains were merged.
 		    	size_t chain_id =
@@ -397,37 +236,26 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 								equivalent_chain.merge(end_point1_other_chain_id, end_point2_other_chain_id));
 				end_point1.chain_id = chain_id;
 				end_point2.chain_id = chain_id;
-				if (! end_point2.on_circle_empty())
-					end_point2.on_circle_detach();
 				assert(validate_graph_and_queue());
-			}
-#ifndef NDEBUG
-			for (EndPoint *end_point : end_points_update) {
-				assert(end_point->edge_out == nullptr);
-				// Point is in the queue.
-				assert(end_point->heap_idx < queue.size());
-				// Point is not connected yet.
-				assert(end_point->chain_id == 0);
-			}
-#endif /* NDEBUG */
-			if (iter == 0) {
-				// Last iteration. There shall be exactly one or two end points waiting to be connected.
-				if (first_point == nullptr) {
-					// Two unconnected points are the end points of the constructed path.
-					assert(end_points_update.size() == 2);
-					first_point = end_points_update.front();
-				} else
-					assert(end_points_update.size() == 1);
-				// Mark both points as ends of the path.
-				for (EndPoint *end_point : end_points_update)
-					end_point->edge_in = end_point->edge_out = nullptr;
-				break;
-			}
-	    	// Update links, distances and queue positions of all points that used to point to end_point1 or end_point2.
-	    	for (EndPoint *end_point : end_points_update) {
-		    	size_t this_idx = end_point - &end_points.front();
+				if (iter == 0) {
+					// Last iteration. There shall be exactly one or two end points waiting to be connected.
+					assert(queue.size() == ((first_point == nullptr) ? 2 : 1));
+					if (first_point == nullptr)
+						first_point = queue.top();
+					while (! queue.empty()) {
+						queue.top()->edge_out = nullptr;
+						queue.pop();
+					}
+					break;
+				}
+	    	} else {
+				// This edge forms a loop. Update end_point1 and try another one.
+				++ iter;
+				end_point1.edge_out = nullptr;
+		    	// Update edge_out and distance.
+		    	size_t this_idx = &end_point1 - &end_points.front();
 		    	// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda).
-				size_t next_idx = find_closest_point(kdtree, end_point->pos, [&end_points, &equivalent_chain, this_idx](size_t idx) { 
+				size_t next_idx = find_closest_point(kdtree, end_point1.pos, [&end_points, &equivalent_chain, this_idx](size_t idx) { 
 			    	assert(end_points[this_idx].edge_out == nullptr);
 			    	assert(end_points[this_idx].chain_id == 0);
 					if ((idx ^ this_idx) <= 1 || end_points[idx].chain_id != 0)
@@ -438,41 +266,97 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 			    	size_t chain2 = equivalent_chain(end_points[idx      ^ 1].chain_id);
 			    	return chain1 != chain2 || chain1 == 0;
 				});
-				assert(next_idx != kdtree.npos);
 				assert(next_idx < end_points.size());
-				EndPoint &end_point2 = end_points[next_idx];
-				end_point->edge_out = &end_point2;
-				if (end_point2.edge_in == nullptr)
-					end_point2.edge_in = end_point;
-				else {
-					assert(end_point->on_circle_empty());
-					assert(end_point2.edge_in->edge_out == &end_point2);
-					end_point->on_circle_merge(end_point2.edge_in);
-				}
-				end_point->distance_out = (end_points[next_idx].pos - end_point->pos).squaredNorm();
+				end_point1.edge_out = &end_points[next_idx];
+				end_point1.distance_out = (end_points[next_idx].pos - end_point1.pos).squaredNorm();
 				// Update position of this end point in the queue based on the distance calculated at the line above.
-				queue.update(end_point->heap_idx);
+				queue.update(end_point1.heap_idx);
 		    	//FIXME Remove the other end point from the KD tree.
 		    	// As the KD tree update is expensive, do it only after some larger number of points is removed from the queue.
 				assert(validate_graph_and_queue());
 	    	}
-			end_points_update.clear();
 		}
-		assert(queue.size() == (first_point == nullptr) ? 1 : 2);
+		assert(queue.empty());
 
 		// Now interconnect pairs of segments into a chain.
 		assert(first_point != nullptr);
 		do {
-			size_t    		 first_point_id      = first_point - &end_points.front();
-			size_t           extrusion_entity_id = first_point_id >> 1;
-			EndPoint 		*second_point        = &end_points[first_point_id ^ 1];
-			ExtrusionEntity *extrusion_entity    = entities[extrusion_entity_id];
-			out.emplace_back(extrusion_entity_id, extrusion_entity->can_reverse() && (first_point_id & 1));
+			assert(out.size() < num_segments);
+			size_t    		 first_point_id = first_point - &end_points.front();
+			size_t           segment_id 	= first_point_id >> 1;
+			EndPoint 		*second_point   = &end_points[first_point_id ^ 1];
+			out.emplace_back(segment_id, (first_point_id & 1) != 0);
 			first_point = second_point->edge_out;
 		} while (first_point != nullptr);
 	}
 
-	assert(out.size() == entities.size());
+	assert(out.size() == num_segments);
+	return out;
+}
+
+std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
+{
+	auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
+	std::vector<std::pair<size_t, bool>> out = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, entities.size(), start_near);
+	for (size_t i = 0; i < entities.size(); ++ i) {
+		ExtrusionEntity *ee = entities[i];
+		if (ee->is_loop())
+			// Ignore reversals for loops, as the start point equals the end point.
+			out[i].second = false;
+		// Is can_reverse() respected by the reversals?
+		assert(entities[i]->can_reverse() || ! out[i].second);
+	}
+	return out;
+}
+
+void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain)
+{
+	assert(entities.size() == chain.size());
+	std::vector<ExtrusionEntity*> out;
+	out.reserve(entities.size());
+    for (const std::pair<size_t, bool> &idx : chain) {
+		assert(entities[idx.first] != nullptr);
+        out.emplace_back(entities[idx.first]);
+        if (idx.second)
+			out.back()->reverse();
+    }
+    entities.swap(out);
+}
+
+void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
+{
+	reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
+}
+
+std::vector<size_t> chain_points(const Points &points, Point *start_near)
+{
+	auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
+	std::vector<size_t> out;
+	out.reserve(ordered.size());
+	for (auto &segment_and_reversal : ordered)
+		out.emplace_back(segment_and_reversal.first);
+	return out;
+}
+
+std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print)
+{
+    // Order objects using a nearest neighbor search.
+    Points object_reference_points;
+    std::vector<std::pair<size_t, size_t>> instances;
+    for (size_t i = 0; i < print.objects().size(); ++ i) {
+    	const PrintObject &object = *print.objects()[i];
+    	for (size_t j = 0; j < object.copies().size(); ++ j) {
+        	object_reference_points.emplace_back(object.copy_center(j));
+        	instances.emplace_back(i, j);
+        }
+    }
+	auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
+    std::vector<std::pair<size_t, size_t>> out;
+	out.reserve(instances.size());
+	for (auto &segment_and_reversal : ordered)
+		out.emplace_back(instances[segment_and_reversal.first]);
 	return out;
 }
 
diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index 2a7e67688..c02e24aee 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -10,7 +10,17 @@
 
 namespace Slic3r {
 
+std::vector<size_t> 				 chain_points(const Points &points, Point *start_near = nullptr);
+
 std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
+void                                 reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
+void                                 chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
+
+// Chain instances of print objects by an approximate shortest path.
+// Returns pairs of PrintObject idx and instance of that PrintObject.
+class Print;
+std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print);
+
 
 } // namespace Slic3r
 

From d06831076d1cbd915d7dd236be44ae4ade9df200 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 26 Sep 2019 17:30:03 +0200
Subject: [PATCH 33/44] WIP: Consolidation of shortest path calculations,
 various chaining algorithms are replaced with the improved TSP algorithm.

---
 src/libslic3r/ClipperUtils.cpp | 21 ++++++-------
 src/libslic3r/Geometry.cpp     | 56 +++-------------------------------
 src/libslic3r/Geometry.hpp     |  3 --
 src/libslic3r/Layer.cpp        |  5 ++-
 src/libslic3r/PrintObject.cpp  | 11 ++-----
 src/libslic3r/ShortestPath.cpp | 16 ++++++++++
 src/libslic3r/ShortestPath.hpp |  4 +++
 xs/xsp/Geometry.xsp            |  5 +--
 8 files changed, 43 insertions(+), 78 deletions(-)

diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp
index 4c6e542f4..c3183bd8e 100644
--- a/src/libslic3r/ClipperUtils.cpp
+++ b/src/libslic3r/ClipperUtils.cpp
@@ -1,5 +1,6 @@
 #include "ClipperUtils.hpp"
 #include "Geometry.hpp"
+#include "ShortestPath.hpp"
 
 // #define CLIPPER_UTILS_DEBUG
 
@@ -671,21 +672,19 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
     // collect ordering points
     Points ordering_points;
     ordering_points.reserve(nodes.size());
-    for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
-        Point p((*it)->Contour.front().X, (*it)->Contour.front().Y);
-        ordering_points.emplace_back(p);
-    }
+    for (ClipperLib::PolyNode *pn : nodes)
+        ordering_points.emplace_back(Point(pn->Contour.front().X, pn->Contour.front().Y));
     
     // perform the ordering
-    ClipperLib::PolyNodes ordered_nodes;
-    Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes);
-    
+    ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes);
+
     // push results recursively
-    for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) {
+    for (ClipperLib::PolyNode *pn : ordered_nodes) {
         // traverse the next depth
-        traverse_pt((*it)->Childs, retval);
-        retval->emplace_back(ClipperPath_to_Slic3rPolygon((*it)->Contour));
-        if ((*it)->IsHole()) retval->back().reverse();  // ccw
+        traverse_pt(pn->Childs, retval);
+        retval->emplace_back(ClipperPath_to_Slic3rPolygon(pn->Contour));
+        if (pn->IsHole())
+        	retval->back().reverse(); // ccw
     }
 }
 
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index cc8a86a96..8cf4047a1 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -309,49 +309,7 @@ convex_hull(const Polygons &polygons)
     return convex_hull(std::move(pp));
 }
 
-/* accepts an arrayref of points and returns a list of indices
-   according to a nearest-neighbor walk */
-void
-chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near)
-{
-    PointConstPtrs my_points;
-    std::map<const Point*,Points::size_type> indices;
-    my_points.reserve(points.size());
-    for (Points::const_iterator it = points.begin(); it != points.end(); ++it) {
-        my_points.push_back(&*it);
-        indices[&*it] = it - points.begin();
-    }
-    
-    retval.reserve(points.size());
-    while (!my_points.empty()) {
-        Points::size_type idx = start_near.nearest_point_index(my_points);
-        start_near = *my_points[idx];
-        retval.push_back(indices[ my_points[idx] ]);
-        my_points.erase(my_points.begin() + idx);
-    }
-}
-
-void
-chained_path(const Points &points, std::vector<Points::size_type> &retval)
-{
-    if (points.empty()) return;  // can't call front() on empty vector
-    chained_path(points, retval, points.front());
-}
-
-/* retval and items must be different containers */
-template<class T>
-void
-chained_path_items(Points &points, T &items, T &retval)
-{
-    std::vector<Points::size_type> indices;
-    chained_path(points, indices);
-    for (std::vector<Points::size_type>::const_iterator it = indices.begin(); it != indices.end(); ++it)
-        retval.push_back(items[*it]);
-}
-template void chained_path_items(Points &points, ClipperLib::PolyNodes &items, ClipperLib::PolyNodes &retval);
-
-bool
-directions_parallel(double angle1, double angle2, double max_diff)
+bool directions_parallel(double angle1, double angle2, double max_diff)
 {
     double diff = fabs(angle1 - angle2);
     max_diff += EPSILON;
@@ -359,8 +317,7 @@ directions_parallel(double angle1, double angle2, double max_diff)
 }
 
 template<class T>
-bool
-contains(const std::vector<T> &vector, const Point &point)
+bool contains(const std::vector<T> &vector, const Point &point)
 {
     for (typename std::vector<T>::const_iterator it = vector.begin(); it != vector.end(); ++it) {
         if (it->contains(point)) return true;
@@ -369,16 +326,14 @@ contains(const std::vector<T> &vector, const Point &point)
 }
 template bool contains(const ExPolygons &vector, const Point &point);
 
-double
-rad2deg_dir(double angle)
+double rad2deg_dir(double angle)
 {
     angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0);
     if (angle < 0) angle += PI;
     return rad2deg(angle);
 }
 
-void
-simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
+void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
 {
     Polygons pp;
     for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) {
@@ -391,8 +346,7 @@ simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
     *retval = Slic3r::simplify_polygons(pp);
 }
 
-double
-linint(double value, double oldmin, double oldmax, double newmin, double newmax)
+double linint(double value, double oldmin, double oldmax, double newmin, double newmax)
 {
     return (value - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin;
 }
diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp
index eec267322..a574209d8 100644
--- a/src/libslic3r/Geometry.hpp
+++ b/src/libslic3r/Geometry.hpp
@@ -138,9 +138,6 @@ Pointf3s convex_hull(Pointf3s points);
 Polygon convex_hull(Points points);
 Polygon convex_hull(const Polygons &polygons);
 
-void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
-void chained_path(const Points &points, std::vector<Points::size_type> &retval);
-template<class T> void chained_path_items(Points &points, T &items, T &retval);
 bool directions_parallel(double angle1, double angle2, double max_diff = 0);
 template<class T> bool contains(const std::vector<T> &vector, const Point &point);
 template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index 4ac64b777..94f114a26 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -1,8 +1,8 @@
 #include "Layer.hpp"
 #include "ClipperUtils.hpp"
-#include "Geometry.hpp"
 #include "Print.hpp"
 #include "Fill/Fill.hpp"
+#include "ShortestPath.hpp"
 #include "SVG.hpp"
 
 #include <boost/log/trivial.hpp>
@@ -57,8 +57,7 @@ void Layer::make_slices()
         ordering_points.push_back(ex.contour.first_point());
     
     // sort slices
-    std::vector<Points::size_type> order;
-    Slic3r::Geometry::chained_path(ordering_points, order);
+    std::vector<Points::size_type> order = chain_points(ordering_points);
     
     // populate slices vector
     for (size_t i : order)
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 2834d9105..4559df8b7 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -12,7 +12,6 @@
 #include <boost/log/trivial.hpp>
 #include <float.h>
 
-#include <tbb/task_scheduler_init.h>
 #include <tbb/parallel_for.h>
 #include <tbb/atomic.h>
 
@@ -75,13 +74,9 @@ PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
 {
     // Order copies with a nearest-neighbor search.
     std::vector<Point> copies;
-    {
-        std::vector<Points::size_type> ordered_copies;
-        Slic3r::Geometry::chained_path(points, ordered_copies);
-        copies.reserve(ordered_copies.size());
-        for (size_t point_idx : ordered_copies)
-            copies.emplace_back(points[point_idx] + m_copies_shift);
-    }
+    copies.reserve(points.size());
+    for (const Point &pt : points)
+        copies.emplace_back(pt + m_copies_shift);
     // Invalidate and set copies.
     PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
     if (copies != m_copies) {
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 79e0fdf11..61762b66d 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -339,6 +339,22 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
 	return out;
 }
 
+template<class T> static inline T chain_path_items(const Points &points, const T &items)
+{
+	auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
+	T out;
+	out.reserve(items.size());
+	for (auto &segment_and_reversal : ordered)
+		out.emplace_back(items[segment_and_reversal.first]);
+	return out;
+}
+
+ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items)
+{
+	return chain_path_items(points, items);
+}
+
 std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print)
 {
     // Order objects using a nearest neighbor search.
diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index c02e24aee..3a1b1af13 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -8,6 +8,8 @@
 #include <utility>
 #include <vector>
 
+namespace ClipperLib { class PolyNode; }
+
 namespace Slic3r {
 
 std::vector<size_t> 				 chain_points(const Points &points, Point *start_near = nullptr);
@@ -16,6 +18,8 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 void                                 reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
 void                                 chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
 
+std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
+
 // Chain instances of print objects by an approximate shortest path.
 // Returns pairs of PrintObject idx and instance of that PrintObject.
 class Print;
diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp
index b7e92ba69..5d6454e8a 100644
--- a/xs/xsp/Geometry.xsp
+++ b/xs/xsp/Geometry.xsp
@@ -3,6 +3,7 @@
 %{
 #include <xsinit.h>
 #include "libslic3r/Geometry.hpp"
+#include "libslic3r/ShortestPath.hpp"
 %}
 
 
@@ -49,7 +50,7 @@ std::vector<Points::size_type>
 chained_path(points)
     Points      points
     CODE:
-        Slic3r::Geometry::chained_path(points, RETVAL);
+        RETVAL = chain_points(points);
     OUTPUT:
         RETVAL
 
@@ -58,7 +59,7 @@ chained_path_from(points, start_from)
     Points      points
     Point*      start_from
     CODE:
-        Slic3r::Geometry::chained_path(points, RETVAL, *start_from);
+        RETVAL = chain_points(points, start_from);
     OUTPUT:
         RETVAL
 

From e65ab90c169bcd5df93b039610a64ebdf0a093b7 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 27 Sep 2019 09:51:07 +0200
Subject: [PATCH 34/44] Fix of G-code path planning: Infill lines were
 incorrectly ordered for islands with another islands in their holes.

Improvement of chaining of infill lines for 3D honeycomb, Gyroid and
Honeycomb infill: New TSP chaining algorithm is used.
---
 src/libslic3r/Fill/Fill3DHoneycomb.cpp | 13 ++++-------
 src/libslic3r/Fill/FillGyroid.cpp      |  7 ++----
 src/libslic3r/Fill/FillHoneycomb.cpp   | 14 +++++------
 src/libslic3r/Fill/FillRectilinear.cpp | 13 ++++-------
 src/libslic3r/GCode.cpp                | 32 ++++++++++++++++++--------
 src/libslic3r/Polyline.cpp             |  6 -----
 src/libslic3r/Polyline.hpp             |  3 ++-
 src/libslic3r/ShortestPath.cpp         | 14 +++++++++++
 src/libslic3r/ShortestPath.hpp         |  2 ++
 9 files changed, 58 insertions(+), 46 deletions(-)

diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
index 6a37e4369..44267d3a4 100644
--- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp
+++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
@@ -1,5 +1,5 @@
 #include "../ClipperUtils.hpp"
-#include "../PolylineCollection.hpp"
+#include "../ShortestPath.hpp"
 #include "../Surface.hpp"
 
 #include "Fill3DHoneycomb.hpp"
@@ -175,27 +175,24 @@ void Fill3DHoneycomb::_fill_surface_single(
                 std::swap(expolygon_off, expolygons_off.front());
             }
         }
-        Polylines chained = PolylineCollection::chained_path_from(
-            std::move(polylines), 
-            PolylineCollection::leftmost_point(polylines), false); // reverse allowed
         bool first = true;
-        for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
+        for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
             if (! first) {
                 // Try to connect the lines.
                 Points &pts_end = polylines_out.back().points;
-                const Point &first_point = it_polyline->points.front();
+                const Point &first_point = polyline.points.front();
                 const Point &last_point = pts_end.back();
                 // TODO: we should also check that both points are on a fill_boundary to avoid 
                 // connecting paths on the boundaries of internal regions
                 if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance && 
                     expolygon_off.contains(Line(last_point, first_point))) {
                     // Append the polyline.
-                    pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
+                    pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
                     continue;
                 }
             }
             // The lines cannot be connected.
-            polylines_out.emplace_back(std::move(*it_polyline));
+            polylines_out.emplace_back(std::move(polyline));
             first = false;
         }
     }
diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp
index 04319bb26..09f0782bc 100644
--- a/src/libslic3r/Fill/FillGyroid.cpp
+++ b/src/libslic3r/Fill/FillGyroid.cpp
@@ -1,5 +1,5 @@
 #include "../ClipperUtils.hpp"
-#include "../PolylineCollection.hpp"
+#include "../ShortestPath.hpp"
 #include "../Surface.hpp"
 #include <cmath>
 #include <algorithm>
@@ -166,11 +166,8 @@ void FillGyroid::_fill_surface_single(
                 std::swap(expolygon_off, expolygons_off.front());
             }
         }
-        Polylines chained = PolylineCollection::chained_path_from(
-            std::move(polylines), 
-            PolylineCollection::leftmost_point(polylines), false); // reverse allowed
         bool first = true;
-        for (Polyline &polyline : chained) {
+        for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
             if (! first) {
                 // Try to connect the lines.
                 Points &pts_end = polylines_out.back().points;
diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp
index cbfe926f2..be569c2d8 100644
--- a/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -1,5 +1,5 @@
 #include "../ClipperUtils.hpp"
-#include "../PolylineCollection.hpp"
+#include "../ShortestPath.hpp"
 #include "../Surface.hpp"
 
 #include "FillHoneycomb.hpp"
@@ -93,22 +93,20 @@ void FillHoneycomb::_fill_surface_single(
 
         // connect paths
         if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
-            Polylines chained = PolylineCollection::chained_path_from(
-                std::move(paths), 
-                PolylineCollection::leftmost_point(paths), false);
+            Polylines chained = chain_infill_polylines(std::move(paths));
             assert(paths.empty());
             paths.clear();
-            for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) {
+            for (Polyline &path : chained) {
                 if (! paths.empty()) {
                     // distance between first point of this path and last point of last path
-                    double distance = (it_path->first_point() - paths.back().last_point()).cast<double>().norm();
+                    double distance = (path.first_point() - paths.back().last_point()).cast<double>().norm();
                     if (distance <= m.hex_width) {
-                        paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end());
+                        paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end());
                         continue;
                     }
                 }
                 // Don't connect the paths.
-                paths.push_back(*it_path);
+                paths.push_back(std::move(path));
             }
         }
         
diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp
index 205eb1b66..d535ba29b 100644
--- a/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/src/libslic3r/Fill/FillRectilinear.cpp
@@ -1,6 +1,6 @@
 #include "../ClipperUtils.hpp"
 #include "../ExPolygon.hpp"
-#include "../PolylineCollection.hpp"
+#include "../ShortestPath.hpp"
 #include "../Surface.hpp"
 
 #include "FillRectilinear.hpp"
@@ -92,15 +92,12 @@ void FillRectilinear::_fill_surface_single(
                 std::swap(expolygon_off, expolygons_off.front());
             }
         }
-        Polylines chained = PolylineCollection::chained_path_from(
-            std::move(polylines), 
-            PolylineCollection::leftmost_point(polylines), false); // reverse allowed
         bool first = true;
-        for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
+        for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
             if (! first) {
                 // Try to connect the lines.
                 Points &pts_end = polylines_out.back().points;
-                const Point &first_point = it_polyline->points.front();
+                const Point &first_point = polyline.points.front();
                 const Point &last_point = pts_end.back();
                 // Distance in X, Y.
                 const Vector distance = last_point - first_point;
@@ -109,12 +106,12 @@ void FillRectilinear::_fill_surface_single(
                 if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && 
                     expolygon_off.contains(Line(last_point, first_point))) {
                     // Append the polyline.
-                    pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
+                    pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
                     continue;
                 }
             }
             // The lines cannot be connected.
-            polylines_out.emplace_back(std::move(*it_polyline));
+            polylines_out.emplace_back(std::move(polyline));
             first = false;
         }
     }
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 8bd42e59a..82a4995c5 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1807,6 +1807,17 @@ void GCode::process_layer(
             layer_surface_bboxes.reserve(n_slices);
             for (const ExPolygon &expoly : layer.slices.expolygons)
                 layer_surface_bboxes.push_back(get_extents(expoly.contour));
+            // Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
+            // so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
+            std::vector<size_t> slices_test_order;
+            slices_test_order.reserve(n_slices);
+            for (size_t i = 0; i < n_slices; ++ i)
+            	slices_test_order.emplace_back(i);
+            std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](int i, int j) {
+            	const Vec2d s1 = layer_surface_bboxes[i].size().cast<double>();
+            	const Vec2d s2 = layer_surface_bboxes[j].size().cast<double>();
+            	return s1.x() * s1.y() < s2.x() * s2.y();
+            });
             auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { 
                 const BoundingBox &bbox = layer_surface_bboxes[i];
                 return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
@@ -1854,16 +1865,19 @@ void GCode::process_layer(
                                     extruder,
                                     &layer_to_print - layers.data(),
                                     layers.size(), n_slices+1);
-                                for (size_t i = 0; i <= n_slices; ++i)
+                                for (size_t i = 0; i <= n_slices; ++ i) {
+									bool   last = i == n_slices;
+                                	size_t island_idx = last ? n_slices : slices_test_order[i];
                                     if (// fill->first_point does not fit inside any slice
-                                        i == n_slices ||
+										last ||
                                         // fill->first_point fits inside ith slice
-                                        point_inside_surface(i, fill->first_point())) {
-                                        if (islands[i].by_region.empty())
-                                            islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
-                                        islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
+                                        point_inside_surface(island_idx, fill->first_point())) {
+                                        if (islands[island_idx].by_region.empty())
+                                            islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
+                                        islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
                                         break;
                                     }
+                                }
                             }
                         }
                     }
@@ -2574,12 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
     std::string gcode;
     for (const ObjectByExtruder::Island::Region &region : by_region) {
         m_config.apply(print.regions()[&region - &by_region.front()]->config());
-		ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false);
-        for (ExtrusionEntity *fill : chained.entities) {
+        for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) {
             auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
             if (eec) {
-				ExtrusionEntityCollection chained2 = eec->chained_path_from(m_last_pos, false);
-				for (ExtrusionEntity *ee : chained2.entities)
+				for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities)
                     gcode += this->extrude_entity(*ee, "infill");
             } else
                 gcode += this->extrude_entity(*fill, "infill");
diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp
index af155468a..531383ae1 100644
--- a/src/libslic3r/Polyline.cpp
+++ b/src/libslic3r/Polyline.cpp
@@ -23,12 +23,6 @@ Polyline::operator Line() const
     return Line(this->points.front(), this->points.back());
 }
 
-Point
-Polyline::last_point() const
-{
-    return this->points.back();
-}
-
 Point
 Polyline::leftmost_point() const
 {
diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp
index e17fafd62..d595f252d 100644
--- a/src/libslic3r/Polyline.hpp
+++ b/src/libslic3r/Polyline.hpp
@@ -62,7 +62,8 @@ public:
 
     operator Polylines() const;
     operator Line() const;
-    Point last_point() const;
+    Point    last_point() const override { return this->points.back(); }
+
     Point leftmost_point() const;
     virtual Lines lines() const;
     void clip_end(double distance);
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 61762b66d..b31a9d1f3 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -339,6 +339,20 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
 	return out;
 }
 
+Polylines chain_infill_polylines(Polylines &polylines)
+{
+	auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
+	Polylines out;
+	out.reserve(polylines.size());
+	for (auto &segment_and_reversal : ordered) {
+		out.emplace_back(std::move(polylines[segment_and_reversal.first]));
+		if (segment_and_reversal.second)
+			out.back().reverse();
+	}
+	return out;	
+}
+
 template<class T> static inline T chain_path_items(const Points &points, const T &items)
 {
 	auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index 3a1b1af13..b4d0b4737 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -18,6 +18,8 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 void                                 reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
 void                                 chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
 
+Polylines 							 chain_infill_polylines(Polylines &src);
+
 std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
 
 // Chain instances of print objects by an approximate shortest path.

From 661c91a8213a88d0783eb784ead47ed168c5dcd5 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 27 Sep 2019 11:31:16 +0200
Subject: [PATCH 35/44] Fixing Linux build GCC 4.8 does not fully support C++11
 and in-class char array initialization

---
 src/admesh/stl.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 43999d365..fa0edec2b 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -90,7 +90,7 @@ struct stl_neighbors {
 
 struct stl_stats {
     stl_stats() { memset(&header, 0, 81); }
-    char          header[81]                = "";
+    char          header[81];
     stl_type      type                      = (stl_type)0;
     uint32_t      number_of_facets          = 0;
     stl_vertex    max                       = stl_vertex::Zero();

From 5123673b3a7ec590e74798b331400a849542b7d8 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 27 Sep 2019 11:42:52 +0200
Subject: [PATCH 36/44] CMakeLists: filename fix so everything works on
 case-sensitive filesystems

---
 src/libslic3r/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 6fe68e56f..5ca87717d 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -100,7 +100,7 @@ add_library(libslic3r STATIC
     Geometry.cpp
     Geometry.hpp
     Int128.hpp
-    KdTreeIndirect.hpp
+    KDTreeIndirect.hpp
     Layer.cpp
     Layer.hpp
     LayerRegion.cpp

From d57a09558e692dc1f0975c8487dc33603762d5d9 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 27 Sep 2019 13:26:58 +0200
Subject: [PATCH 37/44] Several fixes of previous commits related to
 KDTreeIndirect.hpp and ShortestPath.cpp/.hpp

---
 src/libslic3r/KDTreeIndirect.hpp |  7 +++++--
 src/libslic3r/ShortestPath.cpp   | 13 +++++++------
 src/libslic3r/ShortestPath.hpp   |  4 ++--
 3 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp
index f79dab9b3..4f71dd3e4 100644
--- a/src/libslic3r/KDTreeIndirect.hpp
+++ b/src/libslic3r/KDTreeIndirect.hpp
@@ -19,7 +19,10 @@ public:
 	static constexpr size_t NumDimensions = ANumDimensions;
 	using					CoordinateFn  = ACoordinateFn;
 	using					CoordType     = ACoordType;
-	static constexpr size_t npos		  = size_t(-1);
+    // Following could be static constexpr size_t, but that would not link in C++11
+    enum : size_t {
+        npos = size_t(-1)
+    };
 
 	KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
 	KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t>   indices) : coordinate(coordinate) { this->build(std::move(indices)); }
@@ -69,7 +72,7 @@ public:
 	template<typename Visitor>
 	void visit(Visitor &visitor) const
 	{
-		return m_nodes.empty() ? npos : visit_recursive(0, 0, visitor);
+        visit_recursive(0, 0, visitor);
 	}
 
 	CoordinateFn coordinate;
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index b31a9d1f3..490bcf1dc 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -4,6 +4,7 @@
 	#undef assert
 #endif
 
+#include "clipper.hpp"
 #include "ShortestPath.hpp"
 #include "KDTreeIndirect.hpp"
 #include "MutablePriorityQueue.hpp"
@@ -34,7 +35,7 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
 	{
 		// Just sort the end points so that the first point visited is closest to start_near.
 		out.emplace_back(0, start_near != nullptr && 
-			(end_point_func(0, true) - *start_near).cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).cast<double>().squaredNorm());
+            (end_point_func(0, true) - *start_near).template cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).template cast<double>().squaredNorm());
 	} 
 	else
 	{
@@ -55,8 +56,8 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
 	    std::vector<EndPoint> end_points;
 	    end_points.reserve(num_segments * 2);
 	    for (size_t i = 0; i < num_segments; ++ i) {
-	    	end_points.emplace_back(end_point_func(i, true ).cast<double>());
-	    	end_points.emplace_back(end_point_func(i, false).cast<double>());
+            end_points.emplace_back(end_point_func(i, true ).template cast<double>());
+            end_points.emplace_back(end_point_func(i, false).template cast<double>());
 	    }
 
 	    // Construct the closest point KD tree over end points of segments.
@@ -125,7 +126,7 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
 		EndPoint *first_point = nullptr;
 		size_t    first_point_idx = std::numeric_limits<size_t>::max();
 		if (start_near != nullptr) {
-			size_t idx = find_closest_point(kdtree, start_near->cast<double>());
+            size_t idx = find_closest_point(kdtree, start_near->template cast<double>());
 			assert(idx < end_points.size());
 			first_point = &end_points[idx];
 			first_point->distance_out = 0.;
@@ -309,7 +310,7 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 	return out;
 }
 
-void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain)
+void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain)
 {
 	assert(entities.size() == chain.size());
 	std::vector<ExtrusionEntity*> out;
@@ -339,7 +340,7 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
 	return out;
 }
 
-Polylines chain_infill_polylines(Polylines &polylines)
+Polylines chain_infill_polylines(Polylines &&polylines)
 {
 	auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
 	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index b4d0b4737..06081e1fc 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -15,10 +15,10 @@ namespace Slic3r {
 std::vector<size_t> 				 chain_points(const Points &points, Point *start_near = nullptr);
 
 std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
-void                                 reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
+void                                 reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
 void                                 chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
 
-Polylines 							 chain_infill_polylines(Polylines &src);
+Polylines 							 chain_infill_polylines(Polylines &&src);
 
 std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
 

From 331c187b39e9a800eb826267eae09994ca17278d Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 27 Sep 2019 18:17:21 +0200
Subject: [PATCH 38/44] Rest of the path chaining has been replaced with the
 new algorithm. PolylineCollection.cpp/hpp was removed, use Polylines instead.
 Various first_point() / last_point() now return references, not copies.

---
 src/libslic3r/CMakeLists.txt                |   2 -
 src/libslic3r/ExtrusionEntity.hpp           |  16 +--
 src/libslic3r/ExtrusionEntityCollection.cpp |  96 ++++---------
 src/libslic3r/ExtrusionEntityCollection.hpp |   9 +-
 src/libslic3r/Fill/Fill3DHoneycomb.cpp      |   2 +-
 src/libslic3r/Fill/FillGyroid.cpp           |   2 +-
 src/libslic3r/Fill/FillHoneycomb.cpp        |   2 +-
 src/libslic3r/Fill/FillPlanePath.cpp        |   1 -
 src/libslic3r/Fill/FillRectilinear.cpp      |   2 +-
 src/libslic3r/GCode.cpp                     |   6 +-
 src/libslic3r/Geometry.cpp                  |   1 -
 src/libslic3r/KDTreeIndirect.hpp            |   4 +-
 src/libslic3r/Layer.hpp                     |   4 +-
 src/libslic3r/LayerRegion.cpp               |   2 +-
 src/libslic3r/MultiPoint.hpp                |   2 +-
 src/libslic3r/PerimeterGenerator.cpp        |   7 +-
 src/libslic3r/Polygon.cpp                   |  88 ++----------
 src/libslic3r/Polygon.hpp                   |  25 ++--
 src/libslic3r/Polyline.cpp                  |  29 ++--
 src/libslic3r/Polyline.hpp                  |  15 +-
 src/libslic3r/PolylineCollection.cpp        |  92 ------------
 src/libslic3r/PolylineCollection.hpp        |  47 -------
 src/libslic3r/ShortestPath.cpp              | 146 ++++++++++++++++++--
 src/libslic3r/ShortestPath.hpp              |   6 +-
 src/libslic3r/SupportMaterial.cpp           |   2 +-
 xs/xsp/ExtrusionEntityCollection.xsp        |   8 +-
 xs/xsp/Filler.xsp                           |   1 -
 xs/xsp/Layer.xsp                            |   2 -
 xs/xsp/PolylineCollection.xsp               |  11 +-
 29 files changed, 266 insertions(+), 364 deletions(-)
 delete mode 100644 src/libslic3r/PolylineCollection.cpp
 delete mode 100644 src/libslic3r/PolylineCollection.hpp

diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 6fe68e56f..78302e291 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -131,8 +131,6 @@ add_library(libslic3r STATIC
     PolygonTrimmer.hpp
     Polyline.cpp
     Polyline.hpp
-    PolylineCollection.cpp
-    PolylineCollection.hpp
     Print.cpp
     Print.hpp
     PrintBase.cpp
diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index dfc180689..8ec495b6c 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -81,8 +81,8 @@ public:
     virtual ExtrusionEntity* clone_move() = 0;
     virtual ~ExtrusionEntity() {}
     virtual void reverse() = 0;
-    virtual Point first_point() const = 0;
-    virtual Point last_point() const = 0;
+    virtual const Point& first_point() const = 0;
+    virtual const Point& last_point() const = 0;
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0;
@@ -139,8 +139,8 @@ public:
     // Create a new object, initialize it with this object using the move semantics.
 	ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); }
     void reverse() override { this->polyline.reverse(); }
-    Point first_point() const override { return this->polyline.points.front(); }
-    Point last_point() const override { return this->polyline.points.back(); }
+    const Point& first_point() const override { return this->polyline.points.front(); }
+    const Point& last_point() const override { return this->polyline.points.back(); }
     size_t size() const { return this->polyline.size(); }
     bool empty() const { return this->polyline.empty(); }
     bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
@@ -200,8 +200,8 @@ public:
     // Create a new object, initialize it with this object using the move semantics.
 	ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
     void reverse() override;
-    Point first_point() const override { return this->paths.front().polyline.points.front(); }
-    Point last_point() const override { return this->paths.back().polyline.points.back(); }
+    const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
+    const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
     double length() const override;
     ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
@@ -243,8 +243,8 @@ public:
     bool make_clockwise();
     bool make_counter_clockwise();
     void reverse() override;
-    Point first_point() const override { return this->paths.front().polyline.points.front(); }
-    Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
+    const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
+    const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
     Polygon polygon() const;
     double length() const override;
     bool split_at_vertex(const Point &point);
diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp
index f7fab1ba1..8c7e00e60 100644
--- a/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -1,4 +1,5 @@
 #include "ExtrusionEntityCollection.hpp"
+#include "ShortestPath.hpp"
 #include <algorithm>
 #include <cmath>
 #include <map>
@@ -73,78 +74,31 @@ void ExtrusionEntityCollection::remove(size_t i)
     this->entities.erase(this->entities.begin() + i);
 }
 
-ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const
+ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const
 {
-    ExtrusionEntityCollection coll;
-    this->chained_path(&coll, no_reverse, role);
-    return coll;
-}
-
-void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
-{
-    if (this->entities.empty()) return;
-    this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role);
-}
-
-ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
-{
-    ExtrusionEntityCollection coll;
-    this->chained_path_from(start_near, &coll, no_reverse, role);
-    return coll;
-}
-
-void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
-{
-    if (this->no_sort) {
-        *retval = *this;
-        return;
-    }
-
-    retval->entities.reserve(this->entities.size());
-    
-    // if we're asked to return the original indices, build a map
-    std::map<ExtrusionEntity*,size_t> indices_map;
-    
-    ExtrusionEntitiesPtr my_paths;
-    for (ExtrusionEntity * const &entity_src : this->entities) {
-        if (role != erMixed) {
-            // The caller wants only paths with a specific extrusion role.
-            auto role2 = entity_src->role();
-            if (role != role2) {
-                // This extrusion entity does not match the role asked.
-                assert(role2 != erMixed);
-                continue;
-            }
-        }
-
-        ExtrusionEntity *entity = entity_src->clone();
-        my_paths.push_back(entity);
-//        if (orig_indices != nullptr)
-//        	indices_map[entity] = &entity_src - &this->entities.front();
-    }
-    
-    Points endpoints;
-    for (const ExtrusionEntity *entity : my_paths) {
-        endpoints.push_back(entity->first_point());
-        endpoints.push_back((no_reverse || ! entity->can_reverse()) ?
-        	entity->first_point() : entity->last_point());
-    }
-    
-    while (! my_paths.empty()) {
-        // find nearest point
-        int start_index = start_near.nearest_point_index(endpoints);
-        int path_index = start_index/2;
-        ExtrusionEntity* entity = my_paths.at(path_index);
-        // never reverse loops, since it's pointless for chained path and callers might depend on orientation
-        if (start_index % 2 && !no_reverse && entity->can_reverse())
-            entity->reverse();
-        retval->entities.push_back(my_paths.at(path_index));
-//        if (orig_indices != nullptr)
-//        	orig_indices->push_back(indices_map[entity]);
-        my_paths.erase(my_paths.begin() + path_index);
-        endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
-        start_near = retval->entities.back()->last_point();
-    }
+	ExtrusionEntityCollection out;
+	if (this->no_sort) {
+		out = *this;
+	} else {
+		if (role == erMixed)
+			out = *this;
+		else {
+		    for (const ExtrusionEntity *ee : this->entities) {
+		        if (role != erMixed) {
+		            // The caller wants only paths with a specific extrusion role.
+		            auto role2 = ee->role();
+		            if (role != role2) {
+		                // This extrusion entity does not match the role asked.
+		                assert(role2 != erMixed);
+		                continue;
+		            }
+		        }
+		        out.entities.emplace_back(ee->clone());
+		    }
+		}
+		chain_and_reorder_extrusion_entities(out.entities, &start_near);
+	}
+	return out;
 }
 
 void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp
index e92fa156f..4e1491724 100644
--- a/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -65,13 +65,10 @@ public:
     }
     void replace(size_t i, const ExtrusionEntity &entity);
     void remove(size_t i);
-    ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
-    void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
-    ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const;
-    void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
+    ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const;
     void reverse();
-    Point first_point() const { return this->entities.front()->first_point(); }
-    Point last_point() const { return this->entities.back()->last_point(); }
+    const Point& first_point() const { return this->entities.front()->first_point(); }
+    const Point& last_point() const { return this->entities.back()->last_point(); }
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
index 44267d3a4..820f0008b 100644
--- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp
+++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
@@ -176,7 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single(
             }
         }
         bool first = true;
-        for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
+        for (Polyline &polyline : chain_polylines(std::move(polylines))) {
             if (! first) {
                 // Try to connect the lines.
                 Points &pts_end = polylines_out.back().points;
diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp
index 09f0782bc..7cd955892 100644
--- a/src/libslic3r/Fill/FillGyroid.cpp
+++ b/src/libslic3r/Fill/FillGyroid.cpp
@@ -167,7 +167,7 @@ void FillGyroid::_fill_surface_single(
             }
         }
         bool first = true;
-        for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
+        for (Polyline &polyline : chain_polylines(std::move(polylines))) {
             if (! first) {
                 // Try to connect the lines.
                 Points &pts_end = polylines_out.back().points;
diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp
index be569c2d8..948af182b 100644
--- a/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
 
         // connect paths
         if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
-            Polylines chained = chain_infill_polylines(std::move(paths));
+            Polylines chained = chain_polylines(std::move(paths));
             assert(paths.empty());
             paths.clear();
             for (Polyline &path : chained) {
diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp
index 3b9266a0f..7a322ce99 100644
--- a/src/libslic3r/Fill/FillPlanePath.cpp
+++ b/src/libslic3r/Fill/FillPlanePath.cpp
@@ -1,5 +1,4 @@
 #include "../ClipperUtils.hpp"
-#include "../PolylineCollection.hpp"
 #include "../Surface.hpp"
 
 #include "FillPlanePath.hpp"
diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp
index d535ba29b..629e5b6f4 100644
--- a/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/src/libslic3r/Fill/FillRectilinear.cpp
@@ -93,7 +93,7 @@ void FillRectilinear::_fill_surface_single(
             }
         }
         bool first = true;
-        for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
+        for (Polyline &polyline : chain_polylines(std::move(polylines))) {
             if (! first) {
                 // Try to connect the lines.
                 Points &pts_end = polylines_out.back().points;
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 348834b3c..efcf15ad5 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1969,7 +1969,7 @@ void GCode::process_layer(
                     m_layer = layers[instance_to_print.layer_id].support_layer;
                     gcode += this->extrude_support(
                         // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
-                        instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role));
+                        instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
                     m_layer = layers[instance_to_print.layer_id].layer();
                 }
                 for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
@@ -2588,10 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
     std::string gcode;
     for (const ObjectByExtruder::Island::Region &region : by_region) {
         m_config.apply(print.regions()[&region - &by_region.front()]->config());
-        for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) {
+        for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) {
             auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
             if (eec) {
-				for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities)
+				for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
                     gcode += this->extrude_entity(*ee, "infill");
             } else
                 gcode += this->extrude_entity(*fill, "infill");
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index 8cf4047a1..e80b365bb 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -3,7 +3,6 @@
 #include "ClipperUtils.hpp"
 #include "ExPolygon.hpp"
 #include "Line.hpp"
-#include "PolylineCollection.hpp"
 #include "clipper.hpp"
 #include <algorithm>
 #include <cassert>
diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp
index f79dab9b3..e7526210d 100644
--- a/src/libslic3r/KDTreeIndirect.hpp
+++ b/src/libslic3r/KDTreeIndirect.hpp
@@ -61,8 +61,10 @@ public:
 	{
 		CoordType dist = point_coord - this->coordinate(idx, dimension);
 		return (dist * dist < search_radius + CoordType(EPSILON)) ?
+			// The plane intersects a hypersphere centered at point_coord of search_radius.
 			((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
-			(dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
+			// The plane does not intersect the hypersphere.
+			(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
 	}
 
 	// Visitor is supposed to return a bit mask of VisitorReturnMask.
diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp
index 555017207..539ae3925 100644
--- a/src/libslic3r/Layer.hpp
+++ b/src/libslic3r/Layer.hpp
@@ -6,8 +6,6 @@
 #include "SurfaceCollection.hpp"
 #include "ExtrusionEntityCollection.hpp"
 #include "ExPolygonCollection.hpp"
-#include "PolylineCollection.hpp"
-
 
 namespace Slic3r {
 
@@ -48,7 +46,7 @@ public:
     Polygons                    bridged;
 
     // collection of polylines representing the unsupported bridge edges
-    PolylineCollection          unsupported_bridge_edges;
+    Polylines          			unsupported_bridge_edges;
 
     // ordered collection of extrusion paths/loops to build all perimeters
     // (this collection contains only ExtrusionEntityCollection objects)
diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp
index bfe96d311..d13549bf4 100644
--- a/src/libslic3r/LayerRegion.cpp
+++ b/src/libslic3r/LayerRegion.cpp
@@ -272,7 +272,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
                     bridges[idx_last].bridge_angle = bd.angle;
                     if (this->layer()->object()->config().support_material) {
                         polygons_append(this->bridged, bd.coverage());
-                        this->unsupported_bridge_edges.append(bd.unsupported_edges()); 
+                        append(this->unsupported_bridge_edges, bd.unsupported_edges());
                     }
 				} else if (custom_angle > 0) {
 					// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp
index 38020d6e8..7cd6d7834 100644
--- a/src/libslic3r/MultiPoint.hpp
+++ b/src/libslic3r/MultiPoint.hpp
@@ -34,7 +34,7 @@ public:
     void rotate(double angle, const Point &center);
     void reverse();
     Point first_point() const;
-    virtual Point last_point() const = 0;
+    virtual const Point& last_point() const = 0;
     virtual Lines lines() const = 0;
     size_t size() const { return points.size(); }
     bool   empty() const { return points.empty(); }
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index cd5354776..450fff351 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -175,10 +175,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
                 perimeter_generator.overhang_flow.width,
                 perimeter_generator.overhang_flow.height);
             
-            // reapply the nearest point search for starting point
-            // We allow polyline reversal because Clipper may have randomly
-            // reversed polylines during clipping.
-            paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path();
+            // Reapply the nearest point search for starting point.
+            // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
+            chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
         } else {
             ExtrusionPath path(role);
             path.polyline   = loop.polygon.split_at_first_point();
diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp
index cf0783bae..5ef5d0ceb 100644
--- a/src/libslic3r/Polygon.cpp
+++ b/src/libslic3r/Polygon.cpp
@@ -5,43 +5,12 @@
 
 namespace Slic3r {
 
-Polygon::operator Polygons() const
-{
-    Polygons pp;
-    pp.push_back(*this);
-    return pp;
-}
-
-Polygon::operator Polyline() const
-{
-    return this->split_at_first_point();
-}
-
-Point&
-Polygon::operator[](Points::size_type idx)
-{
-    return this->points[idx];
-}
-
-const Point&
-Polygon::operator[](Points::size_type idx) const
-{
-    return this->points[idx];
-}
-
-Point
-Polygon::last_point() const
-{
-    return this->points.front();  // last point == first point for polygons
-}
-
 Lines Polygon::lines() const
 {
     return to_lines(*this);
 }
 
-Polyline
-Polygon::split_at_vertex(const Point &point) const
+Polyline Polygon::split_at_vertex(const Point &point) const
 {
     // find index of point
     for (const Point &pt : this->points)
@@ -52,8 +21,7 @@ Polygon::split_at_vertex(const Point &point) const
 }
 
 // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
-Polyline
-Polygon::split_at_index(int index) const
+Polyline Polygon::split_at_index(int index) const
 {
     Polyline polyline;
     polyline.points.reserve(this->points.size() + 1);
@@ -64,19 +32,6 @@ Polygon::split_at_index(int index) const
     return polyline;
 }
 
-// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
-Polyline
-Polygon::split_at_first_point() const
-{
-    return this->split_at_index(0);
-}
-
-Points
-Polygon::equally_spaced_points(double distance) const
-{
-    return this->split_at_first_point().equally_spaced_points(distance);
-}
-
 /*
 int64_t Polygon::area2x() const
 {
@@ -107,20 +62,17 @@ double Polygon::area() const
     return 0.5 * a;
 }
 
-bool
-Polygon::is_counter_clockwise() const
+bool Polygon::is_counter_clockwise() const
 {
     return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
 }
 
-bool
-Polygon::is_clockwise() const
+bool Polygon::is_clockwise() const
 {
     return !this->is_counter_clockwise();
 }
 
-bool
-Polygon::make_counter_clockwise()
+bool Polygon::make_counter_clockwise()
 {
     if (!this->is_counter_clockwise()) {
         this->reverse();
@@ -129,8 +81,7 @@ Polygon::make_counter_clockwise()
     return false;
 }
 
-bool
-Polygon::make_clockwise()
+bool Polygon::make_clockwise()
 {
     if (this->is_counter_clockwise()) {
         this->reverse();
@@ -139,16 +90,9 @@ Polygon::make_clockwise()
     return false;
 }
 
-bool
-Polygon::is_valid() const
-{
-    return this->points.size() >= 3;
-}
-
 // Does an unoriented polygon contain a point?
 // Tested by counting intersections along a horizontal line.
-bool
-Polygon::contains(const Point &point) const
+bool Polygon::contains(const Point &point) const
 {
     // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
     bool result = false;
@@ -174,8 +118,7 @@ Polygon::contains(const Point &point) const
 }
 
 // this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
-Polygons
-Polygon::simplify(double tolerance) const
+Polygons Polygon::simplify(double tolerance) const
 {
     // repeat first point at the end in order to apply Douglas-Peucker
     // on the whole polygon
@@ -189,8 +132,7 @@ Polygon::simplify(double tolerance) const
     return simplify_polygons(pp);
 }
 
-void
-Polygon::simplify(double tolerance, Polygons &polygons) const
+void Polygon::simplify(double tolerance, Polygons &polygons) const
 {
     Polygons pp = this->simplify(tolerance);
     polygons.reserve(polygons.size() + pp.size());
@@ -198,8 +140,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const
 }
 
 // Only call this on convex polygons or it will return invalid results
-void
-Polygon::triangulate_convex(Polygons* polygons) const
+void Polygon::triangulate_convex(Polygons* polygons) const
 {
     for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) {
         Polygon p;
@@ -214,8 +155,7 @@ Polygon::triangulate_convex(Polygons* polygons) const
 }
 
 // center of mass
-Point
-Polygon::centroid() const
+Point Polygon::centroid() const
 {
     double area_temp = this->area();
     double x_temp = 0;
@@ -232,8 +172,7 @@ Polygon::centroid() const
 
 // find all concave vertices (i.e. having an internal angle greater than the supplied angle)
 // (external = right side, thus we consider ccw orientation)
-Points
-Polygon::concave_points(double angle) const
+Points Polygon::concave_points(double angle) const
 {
     Points points;
     angle = 2*PI - angle;
@@ -256,8 +195,7 @@ Polygon::concave_points(double angle) const
 
 // find all convex vertices (i.e. having an internal angle smaller than the supplied angle)
 // (external = right side, thus we consider ccw orientation)
-Points
-Polygon::convex_points(double angle) const
+Points Polygon::convex_points(double angle) const
 {
     Points points;
     angle = 2*PI - angle;
diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp
index 63162d953..42f938c72 100644
--- a/src/libslic3r/Polygon.hpp
+++ b/src/libslic3r/Polygon.hpp
@@ -13,13 +13,14 @@ namespace Slic3r {
 class Polygon;
 typedef std::vector<Polygon> Polygons;
 
-class Polygon : public MultiPoint {
+class Polygon : public MultiPoint
+{
 public:
-    operator Polygons() const;
-    operator Polyline() const;
-    Point& operator[](Points::size_type idx);
-    const Point& operator[](Points::size_type idx) const;
-    
+    operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
+    operator Polyline() const { return this->split_at_first_point(); }
+    Point& operator[](Points::size_type idx) { return this->points[idx]; }
+    const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
+
     Polygon() {}
     explicit Polygon(const Points &points): MultiPoint(points) {}
     Polygon(const Polygon &other) : MultiPoint(other.points) {}
@@ -34,20 +35,24 @@ public:
     Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
     Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
 
-    Point last_point() const;
+    // last point == first point for polygons
+    const Point& last_point() const override { return this->points.front(); }
+
     virtual Lines lines() const;
     Polyline split_at_vertex(const Point &point) const;
     // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
     Polyline split_at_index(int index) const;
     // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
-    Polyline split_at_first_point() const;
-    Points equally_spaced_points(double distance) const;
+    Polyline split_at_first_point() const { return this->split_at_index(0); }
+    Points   equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); }
+
     double area() const;
     bool is_counter_clockwise() const;
     bool is_clockwise() const;
     bool make_counter_clockwise();
     bool make_clockwise();
-    bool is_valid() const;
+    bool is_valid() const { return this->points.size() >= 3; }
+
     // Does an unoriented polygon contain a point?
     // Tested by counting intersections along a horizontal line.
     bool contains(const Point &point) const;
diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp
index 531383ae1..26aad83d2 100644
--- a/src/libslic3r/Polyline.cpp
+++ b/src/libslic3r/Polyline.cpp
@@ -23,18 +23,17 @@ Polyline::operator Line() const
     return Line(this->points.front(), this->points.back());
 }
 
-Point
-Polyline::leftmost_point() const
+const Point& Polyline::leftmost_point() const
 {
-    Point p = this->points.front();
-    for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
-        if ((*it)(0) < p(0)) p = *it;
+    const Point *p = &this->points.front();
+    for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) {
+        if (it->x() < p->x()) 
+        	p = &(*it);
     }
-    return p;
+    return *p;
 }
 
-Lines
-Polyline::lines() const
+Lines Polyline::lines() const
 {
     Lines lines;
     if (this->points.size() >= 2) {
@@ -205,6 +204,20 @@ BoundingBox get_extents(const Polylines &polylines)
     return bb;
 }
 
+const Point& leftmost_point(const Polylines &polylines)
+{
+    if (polylines.empty())
+        throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
+    Polylines::const_iterator it = polylines.begin();
+    const Point *p = &it->leftmost_point();
+    for (++ it; it != polylines.end(); ++it) {
+        const Point *p2 = &it->leftmost_point();
+        if (p2->x() < p->x())
+            p = p2;
+    }
+    return *p;
+}
+
 bool remove_degenerate(Polylines &polylines)
 {
     bool modified = false;
diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp
index d595f252d..7e3a1e506 100644
--- a/src/libslic3r/Polyline.hpp
+++ b/src/libslic3r/Polyline.hpp
@@ -62,9 +62,9 @@ public:
 
     operator Polylines() const;
     operator Line() const;
-    Point    last_point() const override { return this->points.back(); }
+    const Point& last_point() const override { return this->points.back(); }
 
-    Point leftmost_point() const;
+    const Point& leftmost_point() const;
     virtual Lines lines() const;
     void clip_end(double distance);
     void clip_start(double distance);
@@ -77,6 +77,15 @@ public:
     bool is_straight() const;
 };
 
+// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
+#ifdef PERL_UCHAR_MIN
+class PolylineCollection
+{
+public:
+    Polylines polylines;
+};
+#endif /* PERL_UCHAR_MIN */
+
 extern BoundingBox get_extents(const Polyline &polyline);
 extern BoundingBox get_extents(const Polylines &polylines);
 
@@ -129,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
     }
 }
 
+const Point& leftmost_point(const Polylines &polylines);
+
 bool remove_degenerate(Polylines &polylines);
 
 class ThickPolyline : public Polyline {
diff --git a/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp
deleted file mode 100644
index 0c66c371a..000000000
--- a/src/libslic3r/PolylineCollection.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "PolylineCollection.hpp"
-
-namespace Slic3r {
-
-struct Chaining
-{
-    Point first;
-    Point last;
-    size_t idx;
-};
-
-template<typename T>
-inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &start_near, bool no_reverse)
-{
-    T dmin = std::numeric_limits<T>::max();
-    int idx = 0;
-    for (std::vector<Chaining>::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
-        T d = sqr(T(start_near(0) - it->first(0)));
-        if (d <= dmin) {
-            d += sqr(T(start_near(1) - it->first(1)));
-            if (d < dmin) {
-                idx = (it - pairs.begin()) * 2;
-                dmin = d;
-                if (dmin < EPSILON)
-                    break;
-            }
-        }
-        if (! no_reverse) {
-            d = sqr(T(start_near(0) - it->last(0)));
-            if (d <= dmin) {
-                d += sqr(T(start_near(1) - it->last(1)));
-                if (d < dmin) {
-                    idx = (it - pairs.begin()) * 2 + 1;
-                    dmin = d;
-                    if (dmin < EPSILON)
-                        break;
-                }
-            }
-        }
-    }
-    return idx;
-}
-
-Polylines PolylineCollection::_chained_path_from(
-    const Polylines &src,
-    Point start_near,
-    bool  no_reverse, 
-    bool  move_from_src)
-{
-    std::vector<Chaining> endpoints;
-    endpoints.reserve(src.size());
-    for (size_t i = 0; i < src.size(); ++ i) {
-        Chaining c;
-        c.first = src[i].first_point();
-        if (! no_reverse)
-            c.last = src[i].last_point();
-        c.idx = i;
-        endpoints.push_back(c);
-    }
-    Polylines retval;
-    while (! endpoints.empty()) {
-        // find nearest point
-        int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
-        assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2);
-        if (move_from_src) {
-            retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
-        } else {
-            retval.push_back(src[endpoints[endpoint_index/2].idx]);
-        }
-        if (endpoint_index & 1)
-            retval.back().reverse();
-        endpoints.erase(endpoints.begin() + endpoint_index/2);
-        start_near = retval.back().last_point();
-    }
-    return retval;
-}
-
-Point PolylineCollection::leftmost_point(const Polylines &polylines)
-{
-    if (polylines.empty())
-        throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
-    Polylines::const_iterator it = polylines.begin();
-    Point p = it->leftmost_point();
-    for (++ it; it != polylines.end(); ++it) {
-        Point p2 = it->leftmost_point();
-        if (p2(0) < p(0))
-            p = p2;
-    }
-    return p;
-}
-
-} // namespace Slic3r
diff --git a/src/libslic3r/PolylineCollection.hpp b/src/libslic3r/PolylineCollection.hpp
deleted file mode 100644
index 87fc1985b..000000000
--- a/src/libslic3r/PolylineCollection.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef slic3r_PolylineCollection_hpp_
-#define slic3r_PolylineCollection_hpp_
-
-#include "libslic3r.h"
-#include "Polyline.hpp"
-
-namespace Slic3r {
-
-class PolylineCollection
-{
-    static Polylines _chained_path_from(
-        const Polylines &src,
-        Point start_near,
-        bool no_reverse, 
-        bool move_from_src);
-
-public:
-    Polylines polylines;
-    void chained_path(PolylineCollection* retval, bool no_reverse = false) const
-    	{ retval->polylines = chained_path(this->polylines, no_reverse); }
-    void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const
-    	{ retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); }
-    Point leftmost_point() const
-    	{ return leftmost_point(polylines); }
-    void append(const Polylines &polylines)
-        { this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); }
-
-	static Point     leftmost_point(const Polylines &polylines);
-	static Polylines chained_path(Polylines &&src, bool no_reverse = false) {
-        return (src.empty() || src.front().points.empty()) ?
-            Polylines() :
-            _chained_path_from(src, src.front().first_point(), no_reverse, true);
-    }
-	static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false)
-        { return _chained_path_from(src, start_near, no_reverse, true); }
-    static Polylines chained_path(const Polylines &src, bool no_reverse = false) {
-        return (src.empty() || src.front().points.empty()) ?
-            Polylines() :
-            _chained_path_from(src, src.front().first_point(), no_reverse, false);
-    }
-    static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false)
-        { return _chained_path_from(src, start_near, no_reverse, false); }
-};
-
-}
-
-#endif
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index b31a9d1f3..0fc16e74a 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -14,6 +14,44 @@
 
 namespace Slic3r {
 
+// Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor.
+// This implementation will always produce valid result even if some segments cannot reverse.
+template<typename EndPointType, typename KDTreeType, typename CouldReverseFunc>
+std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<EndPointType> &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point)
+{
+	assert((end_points.size() & 1) == 0);
+	size_t num_segments = end_points.size() / 2;
+	assert(num_segments >= 2);
+	for (EndPointType &ep : end_points)
+		ep.chain_id = 0;
+	std::vector<std::pair<size_t, bool>> out;
+	out.reserve(num_segments);
+	size_t first_point_idx = &first_point - end_points.data();
+	out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0);
+	first_point.chain_id = 1;
+	size_t this_idx = first_point_idx ^ 1;
+	for (int iter = (int)num_segments - 2; iter >= 0; -- iter) {
+		EndPointType &this_point = end_points[this_idx];
+    	this_point.chain_id = 1;
+    	// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda).
+    	// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
+		size_t next_idx = find_closest_point(kdtree, this_point.pos,
+			[this_idx, &end_points, &could_reverse_func](size_t idx) {
+				return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1));
+		});
+		assert(next_idx < end_points.size());
+		EndPointType &end_point = end_points[next_idx];
+		end_point.chain_id = 1;
+		this_idx = next_idx ^ 1;
+	}
+#ifndef _NDEBUG
+	assert(end_points[this_idx].chain_id == 0);
+	for (EndPointType &ep : end_points)
+		assert(&ep == &end_points[this_idx] || ep.chain_id == 1);
+#endif /* _NDEBUG */
+	return out;
+}
+
 // Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm.
 // Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments.
 // Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle.
@@ -22,8 +60,8 @@ namespace Slic3r {
 // The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which 
 // is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates 
 // a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities.
-template<typename PointType, typename SegmentEndPointFunc>
-std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
+template<typename PointType, typename SegmentEndPointFunc, bool REVERSE_COULD_FAIL, typename CouldReverseFunc>
+std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near)
 {
 	std::vector<std::pair<size_t, bool>> out;
 
@@ -132,6 +170,8 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
 			first_point->chain_id = equivalent_chain.next();
 			first_point_idx = idx;
 		}
+		EndPoint *initial_point = first_point;
+		EndPoint *last_point = nullptr;
 
 		// Assign the closest point and distance to the end points.
 		for (EndPoint &end_point : end_points) {
@@ -240,12 +280,15 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
 				if (iter == 0) {
 					// Last iteration. There shall be exactly one or two end points waiting to be connected.
 					assert(queue.size() == ((first_point == nullptr) ? 2 : 1));
-					if (first_point == nullptr)
+					if (first_point == nullptr) {
 						first_point = queue.top();
-					while (! queue.empty()) {
-						queue.top()->edge_out = nullptr;
 						queue.pop();
+						first_point->edge_out = nullptr;
 					}
+					last_point = queue.top();
+					last_point->edge_out = nullptr;
+					queue.pop();
+					assert(queue.empty());
 					break;
 				}
 	    	} else {
@@ -280,24 +323,77 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
 
 		// Now interconnect pairs of segments into a chain.
 		assert(first_point != nullptr);
+		out.reserve(num_segments);
+		bool      failed = false;
 		do {
 			assert(out.size() < num_segments);
 			size_t    		 first_point_id = first_point - &end_points.front();
 			size_t           segment_id 	= first_point_id >> 1;
+			bool             reverse        = (first_point_id & 1) != 0;
 			EndPoint 		*second_point   = &end_points[first_point_id ^ 1];
-			out.emplace_back(segment_id, (first_point_id & 1) != 0);
+			if (REVERSE_COULD_FAIL) {
+				if (reverse && ! could_reverse_func(segment_id)) {
+					failed = true;
+					break;
+				}
+			} else {
+				assert(! reverse || could_reverse_func(segment_id));
+			}
+			out.emplace_back(segment_id, reverse);
 			first_point = second_point->edge_out;
 		} while (first_point != nullptr);
+		if (REVERSE_COULD_FAIL) {
+			if (failed) {
+				if (start_near == nullptr) {
+					// We may try the reverse order.
+					out.clear();
+					first_point = last_point;
+					failed = false;
+					do {
+						assert(out.size() < num_segments);
+						size_t    		 first_point_id = first_point - &end_points.front();
+						size_t           segment_id 	= first_point_id >> 1;
+						bool             reverse        = (first_point_id & 1) != 0;
+						EndPoint 		*second_point   = &end_points[first_point_id ^ 1];
+						if (reverse && ! could_reverse_func(segment_id)) {
+							failed = true;
+							break;
+						}
+						out.emplace_back(segment_id, reverse);
+						first_point = second_point->edge_out;
+					} while (first_point != nullptr);
+				}
+			}
+			if (failed)
+				// As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints.
+				out = chain_segments_closest_point<EndPoint, decltype(kdtree), CouldReverseFunc>(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front());
+		} else {
+			assert(! failed);
+		}
 	}
 
 	assert(out.size() == num_segments);
 	return out;
 }
 
+template<typename PointType, typename SegmentEndPointFunc, typename CouldReverseFunc>
+std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near)
+{
+	return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, true, CouldReverseFunc>(end_point_func, could_reverse_func, num_segments, start_near);
+}
+
+template<typename PointType, typename SegmentEndPointFunc>
+std::vector<std::pair<size_t, bool>> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
+{
+	auto could_reverse_func = [](size_t /* idx */) -> bool { return true; };
+	return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, false, decltype(could_reverse_func)>(end_point_func, could_reverse_func, num_segments, start_near);
+}
+
 std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
 {
 	auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
-	std::vector<std::pair<size_t, bool>> out = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, entities.size(), start_near);
+	auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
+	std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
 	for (size_t i = 0; i < entities.size(); ++ i) {
 		ExtrusionEntity *ee = entities[i];
 		if (ee->is_loop())
@@ -328,10 +424,34 @@ void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entitie
 	reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
 }
 
+std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
+{
+	auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
+	return chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near);
+}
+
+void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain)
+{
+	assert(extrusion_paths.size() == chain.size());
+	std::vector<ExtrusionPath> out;
+	out.reserve(extrusion_paths.size());
+    for (const std::pair<size_t, bool> &idx : chain) {
+        out.emplace_back(std::move(extrusion_paths[idx.first]));
+        if (idx.second)
+			out.back().reverse();
+    }
+    extrusion_paths.swap(out);
+}
+
+void chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
+{
+	reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, start_near));
+}
+
 std::vector<size_t> chain_points(const Points &points, Point *start_near)
 {
 	auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
-	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
 	std::vector<size_t> out;
 	out.reserve(ordered.size());
 	for (auto &segment_and_reversal : ordered)
@@ -339,12 +459,12 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
 	return out;
 }
 
-Polylines chain_infill_polylines(Polylines &polylines)
+Polylines chain_polylines(Polylines &polylines, const Point *start_near)
 {
 	auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
-	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), start_near);
 	Polylines out;
-	out.reserve(polylines.size());
+	out.reserve(polylines.size()); 
 	for (auto &segment_and_reversal : ordered) {
 		out.emplace_back(std::move(polylines[segment_and_reversal.first]));
 		if (segment_and_reversal.second)
@@ -356,7 +476,7 @@ Polylines chain_infill_polylines(Polylines &polylines)
 template<class T> static inline T chain_path_items(const Points &points, const T &items)
 {
 	auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
-	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
 	T out;
 	out.reserve(items.size());
 	for (auto &segment_and_reversal : ordered)
@@ -382,7 +502,7 @@ std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print
         }
     }
 	auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
-	std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
+	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
     std::vector<std::pair<size_t, size_t>> out;
 	out.reserve(instances.size());
 	for (auto &segment_and_reversal : ordered)
diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index b4d0b4737..81e2c20e1 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -18,7 +18,11 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 void                                 reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
 void                                 chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
 
-Polylines 							 chain_infill_polylines(Polylines &src);
+std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
+void                                 reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain);
+void                                 chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
+
+Polylines 							 chain_polylines(Polylines &src, const Point *start_near = nullptr);
 
 std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
 
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index c0c8e9ea0..a6648f108 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -923,7 +923,7 @@ namespace SupportMaterialInternal {
         //FIXME add supports at regular intervals to support long bridges!
         bridges = diff(bridges,
                 // Offset unsupported edges into polygons.
-                offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
         // Remove bridged areas from the supported areas.
         contact_polygons = diff(contact_polygons, bridges, true);
     }
diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp
index 1f16ae3d4..a01788c56 100644
--- a/xs/xsp/ExtrusionEntityCollection.xsp
+++ b/xs/xsp/ExtrusionEntityCollection.xsp
@@ -14,13 +14,17 @@
     void clear();
     ExtrusionEntityCollection* chained_path(bool no_reverse, ExtrusionRole role = erMixed)
         %code{%
+            if (no_reverse)
+                croak("no_reverse must be false");
             RETVAL = new ExtrusionEntityCollection();
-            THIS->chained_path(RETVAL, no_reverse, role);
+            *RETVAL = THIS->chained_path_from(THIS->entities.front()->first_point());
         %};
     ExtrusionEntityCollection* chained_path_from(Point* start_near, bool no_reverse, ExtrusionRole role = erMixed)
         %code{%
+            if (no_reverse)
+                croak("no_reverse must be false");
             RETVAL = new ExtrusionEntityCollection();
-            THIS->chained_path_from(*start_near, RETVAL, no_reverse, role);
+            *RETVAL = THIS->chained_path_from(*start_near, role);
         %};
     Clone<Point> first_point();
     Clone<Point> last_point();
diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp
index 34a6d33be..647d851eb 100644
--- a/xs/xsp/Filler.xsp
+++ b/xs/xsp/Filler.xsp
@@ -3,7 +3,6 @@
 %{
 #include <xsinit.h>
 #include "libslic3r/Fill/Fill.hpp"
-#include "libslic3r/PolylineCollection.hpp"
 #include "libslic3r/ExtrusionEntity.hpp"
 #include "libslic3r/ExtrusionEntityCollection.hpp"
 %}
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index efd6c9ae6..fc94d5115 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -19,8 +19,6 @@
         %code%{ RETVAL = &THIS->fill_surfaces; %};
     Polygons bridged()
         %code%{ RETVAL = THIS->bridged; %};
-    Ref<PolylineCollection> unsupported_bridge_edges()
-        %code%{ RETVAL = &THIS->unsupported_bridge_edges; %};
     Ref<ExtrusionEntityCollection> perimeters()
         %code%{ RETVAL = &THIS->perimeters; %};
     Ref<ExtrusionEntityCollection> fills()
diff --git a/xs/xsp/PolylineCollection.xsp b/xs/xsp/PolylineCollection.xsp
index 7237be5bb..d8bb41b49 100644
--- a/xs/xsp/PolylineCollection.xsp
+++ b/xs/xsp/PolylineCollection.xsp
@@ -2,7 +2,11 @@
 
 %{
 #include <xsinit.h>
-#include "libslic3r/PolylineCollection.hpp"
+
+#include "libslic3r.h"
+#include "Polyline.hpp"
+#include "ShortestPath.hpp"
+
 %}
 
 %name{Slic3r::Polyline::Collection} class PolylineCollection {
@@ -14,16 +18,15 @@
     PolylineCollection* chained_path(bool no_reverse)
         %code{%
             RETVAL = new PolylineCollection();
-            THIS->chained_path(RETVAL, no_reverse);
+            RETVAL->polylines = chain_polylines(THIS->polylines, &THIS->polylines.front().first_point());
         %};
     PolylineCollection* chained_path_from(Point* start_near, bool no_reverse)
         %code{%
             RETVAL = new PolylineCollection();
-            THIS->chained_path_from(*start_near, RETVAL, no_reverse);
+            RETVAL->polylines = chain_polylines(THIS->polylines, start_near);
         %};
     int count()
         %code{% RETVAL = THIS->polylines.size(); %};
-    Clone<Point> leftmost_point();
 %{
 
 PolylineCollection*

From 50d3894e3298ebce4c826c888d35e964bafc7867 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 27 Sep 2019 18:47:08 +0200
Subject: [PATCH 39/44] Fix of the ShortestPath.hpp interface: Provide non-move
 variant.

---
 src/libslic3r/ShortestPath.hpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index 1b91b59cb..b06d165d3 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -23,6 +23,7 @@ void                                 reorder_extrusion_paths(std::vector<Extrusi
 void                                 chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
 
 Polylines 							 chain_polylines(Polylines &&src, const Point *start_near = nullptr);
+inline Polylines 					 chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp)); }
 
 std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
 

From debd9e2f81afebe665fe391aa99107d58550be36 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 27 Sep 2019 19:07:07 +0200
Subject: [PATCH 40/44] One more try, fixing a missing parameter.

---
 src/libslic3r/ShortestPath.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp
index b06d165d3..cd342015d 100644
--- a/src/libslic3r/ShortestPath.hpp
+++ b/src/libslic3r/ShortestPath.hpp
@@ -23,7 +23,7 @@ void                                 reorder_extrusion_paths(std::vector<Extrusi
 void                                 chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
 
 Polylines 							 chain_polylines(Polylines &&src, const Point *start_near = nullptr);
-inline Polylines 					 chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp)); }
+inline Polylines 					 chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); }
 
 std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
 

From 6d11bfe96a96058f6d81f16f023d275f264883ba Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 27 Sep 2019 19:18:35 +0200
Subject: [PATCH 41/44] Fixing C++ issues, that the Visual Studio compiler did
 not report.

---
 src/libslic3r/ShortestPath.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 3e6cf166a..a0fb05005 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -45,11 +45,11 @@ std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<En
 		end_point.chain_id = 1;
 		this_idx = next_idx ^ 1;
 	}
-#ifndef _NDEBUG
+#ifndef NDEBUG
 	assert(end_points[this_idx].chain_id == 0);
 	for (EndPointType &ep : end_points)
 		assert(&ep == &end_points[this_idx] || ep.chain_id == 1);
-#endif /* _NDEBUG */
+#endif /* NDEBUG */
 	return out;
 }
 
@@ -431,7 +431,7 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<Extrusion
 	return chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near);
 }
 
-void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain)
+void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const std::vector<std::pair<size_t, bool>> &chain)
 {
 	assert(extrusion_paths.size() == chain.size());
 	std::vector<ExtrusionPath> out;

From 0abde9a2a845aa42d79cee500b6bc4c6a93b82c7 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 27 Sep 2019 19:47:30 +0200
Subject: [PATCH 42/44] Return MultiPoint::first_point() by reference.

---
 src/libslic3r/MultiPoint.cpp | 36 +++++++-----------------------------
 src/libslic3r/MultiPoint.hpp |  8 +++++---
 2 files changed, 12 insertions(+), 32 deletions(-)

diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp
index ee3b99747..39b07e7d8 100644
--- a/src/libslic3r/MultiPoint.cpp
+++ b/src/libslic3r/MultiPoint.cpp
@@ -3,11 +3,6 @@
 
 namespace Slic3r {
 
-MultiPoint::operator Points() const
-{
-    return this->points;
-}
-
 void MultiPoint::scale(double factor)
 {
     for (Point &pt : points)
@@ -57,18 +52,7 @@ void MultiPoint::rotate(double angle, const Point &center)
     }
 }
 
-void MultiPoint::reverse()
-{
-    std::reverse(this->points.begin(), this->points.end());
-}
-
-Point MultiPoint::first_point() const
-{
-    return this->points.front();
-}
-
-double
-MultiPoint::length() const
+double MultiPoint::length() const
 {
     Lines lines = this->lines();
     double len = 0;
@@ -78,8 +62,7 @@ MultiPoint::length() const
     return len;
 }
 
-int
-MultiPoint::find_point(const Point &point) const
+int MultiPoint::find_point(const Point &point) const
 {
     for (const Point &pt : this->points)
         if (pt == point)
@@ -87,21 +70,18 @@ MultiPoint::find_point(const Point &point) const
     return -1;  // not found
 }
 
-bool
-MultiPoint::has_boundary_point(const Point &point) const
+bool MultiPoint::has_boundary_point(const Point &point) const
 {
     double dist = (point.projection_onto(*this) - point).cast<double>().norm();
     return dist < SCALED_EPSILON;
 }
 
-BoundingBox
-MultiPoint::bounding_box() const
+BoundingBox MultiPoint::bounding_box() const
 {
     return BoundingBox(this->points);
 }
 
-bool 
-MultiPoint::has_duplicate_points() const
+bool MultiPoint::has_duplicate_points() const
 {
     for (size_t i = 1; i < points.size(); ++i)
         if (points[i-1] == points[i])
@@ -109,8 +89,7 @@ MultiPoint::has_duplicate_points() const
     return false;
 }
 
-bool
-MultiPoint::remove_duplicate_points()
+bool MultiPoint::remove_duplicate_points()
 {
     size_t j = 0;
     for (size_t i = 1; i < points.size(); ++i) {
@@ -129,8 +108,7 @@ MultiPoint::remove_duplicate_points()
     return false;
 }
 
-bool
-MultiPoint::intersection(const Line& line, Point* intersection) const
+bool MultiPoint::intersection(const Line& line, Point* intersection) const
 {
     Lines lines = this->lines();
     for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) {
diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp
index 7cd6d7834..9ff91b502 100644
--- a/src/libslic3r/MultiPoint.hpp
+++ b/src/libslic3r/MultiPoint.hpp
@@ -17,7 +17,8 @@ class MultiPoint
 public:
     Points points;
     
-    operator Points() const;
+    operator Points() const { return this->points; }
+
     MultiPoint() {}
     MultiPoint(const MultiPoint &other) : points(other.points) {}
     MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
@@ -32,8 +33,9 @@ public:
     void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
     void rotate(double cos_angle, double sin_angle);
     void rotate(double angle, const Point &center);
-    void reverse();
-    Point first_point() const;
+    void reverse() { std::reverse(this->points.begin(), this->points.end()); }
+
+    const Point& first_point() const { return this->points.front(); }
     virtual const Point& last_point() const = 0;
     virtual Lines lines() const = 0;
     size_t size() const { return points.size(); }

From 5ded376fe2a0ef897e9ee6b8d2f8f82dca17848c Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Sat, 28 Sep 2019 09:57:26 +0200
Subject: [PATCH 43/44] An attempt to fix #2997 More fixes might be needed on
 the other platforms

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 19dedc7b0..0835a0d40 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -131,7 +131,7 @@ ObjectList::ObjectList(wxWindow* parent) :
         {
             wxDataViewItemArray sels;
             GetSelections(sels);
-            if (sels.front() == m_last_selected_item)
+            if (! sels.empty() && sels.front() == m_last_selected_item)
                 m_last_selected_item = sels.back();
             else
                 m_last_selected_item = event.GetItem();

From 904bbcc006d2d7acb85da93e2c9f74074649fc58 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 30 Sep 2019 13:59:26 +0200
Subject: [PATCH 44/44] Fix of SPE-1035 (Wrong filament color updating for
 multi-material print)

---
 src/slic3r/GUI/Plater.cpp | 28 ++++++++++++++++++++++++++++
 src/slic3r/GUI/Plater.hpp |  1 +
 src/slic3r/GUI/Tab.cpp    |  6 ++++++
 3 files changed, 35 insertions(+)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 97c292703..b172ad489 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -4842,6 +4842,34 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
         this->p->schedule_background_process();
 }
 
+void Plater::force_filament_colors_update()
+{
+    bool update_scheduled = false;
+    DynamicPrintConfig* config = p->config;
+    const std::vector<std::string> filament_presets = wxGetApp().preset_bundle->filament_presets;
+    if (filament_presets.size() > 1 && 
+        p->config->option<ConfigOptionStrings>("filament_colour")->values.size() == filament_presets.size())
+    {
+        const PresetCollection& filaments = wxGetApp().preset_bundle->filaments;
+        std::vector<std::string> filament_colors;
+        filament_colors.reserve(filament_presets.size());
+
+        for (const std::string& filament_preset : filament_presets)
+            filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0));
+
+        if (config->option<ConfigOptionStrings>("filament_colour")->values != filament_colors) {
+            config->option<ConfigOptionStrings>("filament_colour")->values = filament_colors;
+            update_scheduled = true;
+        }
+    }
+
+    if (update_scheduled)
+        update();
+
+    if (p->main_frame->is_loaded())
+        this->p->schedule_background_process();
+}
+
 void Plater::on_activate()
 {
 #ifdef __linux__
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 26dcb5ac3..93df68738 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -211,6 +211,7 @@ public:
 
     void on_extruders_change(size_t extruders_count);
     void on_config_change(const DynamicPrintConfig &config);
+    void force_filament_colors_update();
     // On activating the parent window.
     void on_activate();
     const DynamicPrintConfig* get_plater_config() const;
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index c87626a48..803a5cf08 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3032,6 +3032,12 @@ void Tab::save_preset(std::string name /*= ""*/)
     if (m_type == Preset::TYPE_PRINTER)
         static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count;
     update_changed_ui();
+
+    /* If filament preset is saved for multi-material printer preset, 
+     * there are cases when filament comboboxs are updated for old (non-modified) colors, 
+     * but in full_config a filament_colors option aren't.*/
+    if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1)
+        wxGetApp().plater()->force_filament_colors_update();
 }
 
 // Called for a currently selected preset.