diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 483fd36f9..7510d22af 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -356,28 +356,4 @@ sub set_menu_item_icon {
     }
 }
 
-sub save_window_pos {
-    my ($self, $window, $name) = @_;
-    
-    $self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY);
-    $self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH);
-    $self->{app_config}->set("${name}_maximized", $window->IsMaximized);
-    $self->{app_config}->save;
-}
-
-sub restore_window_pos {
-    my ($self, $window, $name) = @_;
-    if ($self->{app_config}->has("${name}_pos")) {
-        my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ];
-        $window->SetSize($size);
-        
-        my $display = Wx::Display->new->GetClientArea();
-        my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ];
-        if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) {
-            $window->Move($pos);
-        }
-        $window->Maximize(1) if $self->{app_config}->get("${name}_maximized");
-    }
-}
-
 1;
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index 8928ad988..9209299ab 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -74,7 +74,8 @@ sub new {
     $self->{statusbar}->Embed;
     $self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
     # Make the global status bar and its progress indicator available in C++
-    $appController->set_global_progress_indicator($self->{statusbar});
+#FIXME Vojtech: Merging error
+#    $appController->set_global_progress_indicator($self->{statusbar});
 
     $appController->set_model($self->{plater}->{model});
     $appController->set_print($self->{plater}->{print});
@@ -92,7 +93,7 @@ sub new {
         $self->Fit;
         $self->SetMinSize([760, 490]);
         $self->SetSize($self->GetMinSize);
-        wxTheApp->restore_window_pos($self, "main_frame");
+        Slic3r::GUI::restore_window_size($self, "main_frame");
         $self->Show;
         $self->Layout;
     }
@@ -105,7 +106,7 @@ sub new {
             return;
         }
         # save window size
-        wxTheApp->save_window_pos($self, "main_frame");
+        Slic3r::GUI::save_window_size($self, "main_frame");
         # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
         # but in rare cases it may not have been called yet.
         wxTheApp->{app_config}->save;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 59a4567be..ac3e445d1 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1804,20 +1804,37 @@ sub print_info_box_show {
         $grid_sizer->AddGrowableCol(1, 1);
         $grid_sizer->AddGrowableCol(3, 1);
         $print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
+        my $is_wipe_tower = $self->{print}->total_wipe_tower_filament > 0;
         my @info = (
             L("Used Filament (m)")
-                => sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+                =>  $is_wipe_tower ?
+                       sprintf("%.2f  (%.2f %s + %.2f %s)" , $self->{print}->total_used_filament / 1000,
+                                                            ($self->{print}->total_used_filament - $self->{print}->total_wipe_tower_filament) / 1000,
+                                                             L("objects"),
+							     $self->{print}->total_wipe_tower_filament / 1000,
+                                                             L("wipe tower")) :
+                       sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+
             L("Used Filament (mm³)")
                 => sprintf("%.2f" , $self->{print}->total_extruded_volume),
             L("Used Filament (g)"),
                 => sprintf("%.2f" , $self->{print}->total_weight),
             L("Cost"),
-                => sprintf("%.2f" , $self->{print}->total_cost),
+                => $is_wipe_tower ?
+                       sprintf("%.2f  (%.2f %s + %.2f %s)" , $self->{print}->total_cost,
+                                                            ($self->{print}->total_cost - $self->{print}->total_wipe_tower_cost),
+                                                             L("objects"),
+							     $self->{print}->total_wipe_tower_cost,
+                                                             L("wipe tower")) :
+                       sprintf("%.2f" , $self->{print}->total_cost),
             L("Estimated printing time (normal mode)")
                 => $self->{print}->estimated_normal_print_time,
             L("Estimated printing time (silent mode)")
                 => $self->{print}->estimated_silent_print_time
         );
+        # if there is a wipe tower, insert number of toolchanges info into the array:
+        splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->m_wipe_tower_number_of_toolchanges))  if ($is_wipe_tower);
+
         while ( my $label = shift @info) {
             my $value = shift @info;
             next if $value eq "N/A";
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 26a6fdec3..77efbb29b 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -238,7 +238,7 @@ sub _update {
             my @expolygons = ();
             foreach my $volume (@{$self->{model_object}->volumes}) {
                 next if !$volume->mesh;
-                next if $volume->modifier;
+                next if !$volume->model_part;
                 my $expp = $volume->mesh->slice([ $z_cut ])->[0];
                 push @expolygons, @$expp;
             }
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 4032886f3..fd02a030f 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -16,6 +16,8 @@ use base 'Wx::Panel';
 use constant ICON_OBJECT        => 0;
 use constant ICON_SOLIDMESH     => 1;
 use constant ICON_MODIFIERMESH  => 2;
+use constant ICON_SUPPORT_ENFORCER => 3;
+use constant ICON_SUPPORT_BLOCKER => 4;
 
 sub new {
     my ($class, $parent, %params) = @_;
@@ -35,7 +37,7 @@ sub new {
         y               => 0,
         z               => 0,
     };
-    
+
     # create TreeCtrl
     my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], 
         wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT
@@ -46,6 +48,8 @@ sub new {
         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG));     # ICON_OBJECT
         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG));   # ICON_SOLIDMESH
         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG));    # ICON_MODIFIERMESH
+        $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_enforcer.png"), wxBITMAP_TYPE_PNG));    # ICON_SUPPORT_ENFORCER
+        $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_blocker.png"), wxBITMAP_TYPE_PNG));    # ICON_SUPPORT_BLOCKER
         
         my $rootId = $tree->AddRoot("Object", ICON_OBJECT);
         $tree->SetPlData($rootId, { type => 'object' });
@@ -89,7 +93,14 @@ sub new {
     $self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
     
     # part settings panel
-    $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
+    $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub {
+        my ($key, $value) = @_;
+        wxTheApp->CallAfter(sub { 
+            $self->set_part_type($value) if ($key eq "part_type");
+            $self->{part_settings_changed} = 1;
+            $self->_update_canvas; 
+        });
+    });
     my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
     $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
 
@@ -225,8 +236,11 @@ sub reload_tree {
     my $selectedId = $rootId;
     foreach my $volume_id (0..$#{$object->volumes}) {
         my $volume = $object->volumes->[$volume_id];
-        
-        my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
+        my $icon = 
+            $volume->modifier ? ICON_MODIFIERMESH : 
+            $volume->support_enforcer ? ICON_SUPPORT_ENFORCER :
+            $volume->support_blocker ? ICON_SUPPORT_BLOCKER :
+            ICON_SOLIDMESH;
         my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon);
         if ($volume_id == $selected_volume_idx) {
             $selectedId = $itemId;
@@ -288,6 +302,8 @@ sub selection_changed {
     
     if (my $itemData = $self->get_selection) {
         my ($config, @opt_keys);
+        my $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_OBJECT;
+        my $support = 0;
         if ($itemData->{type} eq 'volume') {
             # select volume in 3D preview
             if ($self->{canvas}) {
@@ -301,16 +317,24 @@ sub selection_changed {
             # attach volume config to settings panel
             my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
    
-            if ($volume->modifier) {
+            if (! $volume->model_part) {
                 $self->{optgroup_movers}->enable;
+                if ($volume->support_enforcer || $volume->support_blocker) {
+                    $support = 1;
+                    $type = $volume->support_enforcer ? 
+                        Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER :
+                        Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER;
+                } else {
+                    $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER;
+                }
             } else {
+                $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART;
                 $self->{optgroup_movers}->disable;
             }
             $config = $volume->config;
             $self->{staticbox}->SetLabel('Part Settings');
-            
             # get default values
-            @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys};
+            @opt_keys = $support ? () : @{Slic3r::Config::PrintRegion->new->get_keys};
         } elsif ($itemData->{type} eq 'object') {
             # select nothing in 3D preview
             
@@ -323,33 +347,54 @@ sub selection_changed {
         # get default values
         my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
 
-       # decide which settings will be shown by default
+        # decide which settings will be shown by default
         if ($itemData->{type} eq 'object') {
             $config->set_ifndef('wipe_into_objects', 0);
             $config->set_ifndef('wipe_into_infill', 0);
         }
 
         # append default extruder
-        push @opt_keys, 'extruder';
-        $default_config->set('extruder', 0);
-        $config->set_ifndef('extruder', 0);
+        if (! $support) {
+            push @opt_keys, 'extruder';
+            $default_config->set('extruder', 0);
+            $config->set_ifndef('extruder', 0);
+        }
+        $self->{settings_panel}->set_type($type);
         $self->{settings_panel}->set_default_config($default_config);
         $self->{settings_panel}->set_config($config);
         $self->{settings_panel}->set_opt_keys(\@opt_keys);
 
         # disable minus icon to remove the settings
-        if ($itemData->{type} eq 'object') {
-            $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
-	} else {
-            $self->{settings_panel}->set_fixed_options([qw(extruder)]);
-        }
-
+        my $fixed_options = 
+            ($itemData->{type} eq 'object') ? [qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)] :
+            $support ? [] : [qw(extruder)];
+        $self->{settings_panel}->set_fixed_options($fixed_options);
         $self->{settings_panel}->enable;
     }
     
     Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
 }
 
+sub set_part_type
+{
+    my ($self, $part_type) = @_;
+    if (my $itemData = $self->get_selection) {
+        if ($itemData->{type} eq 'volume') {
+            my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
+            if ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER ||
+                $part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART) {            
+                $volume->set_modifier($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER);
+            } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER) {
+                $volume->set_support_enforcer;
+            } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER) {
+                $volume->set_support_blocker;
+            }
+            # We want the icon of the selected item to be changed as well.
+            $self->reload_tree($itemData->{volume_id});
+        }
+    }
+}
+
 sub on_btn_load {
     my ($self, $is_modifier) = @_;
     
diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
index 3befba708..3ccf1d7f8 100644
--- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
@@ -33,7 +33,7 @@ sub new {
         $self->{layers}->Closing;
         
         # save window size
-        wxTheApp->save_window_pos($self, "object_settings");
+        Slic3r::GUI::save_window_size($self, "object_settings");
         
         $self->EndModal(wxID_OK);
         $self->{parts}->Destroy;
@@ -49,7 +49,7 @@ sub new {
     
     $self->Layout;
     
-    wxTheApp->restore_window_pos($self, "object_settings");
+    Slic3r::GUI::restore_window_size($self, "object_settings");
     
     return $self;
 }
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index ea4ce7132..b085871f0 100644
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -7,15 +7,20 @@ use warnings;
 use utf8;
 
 use List::Util qw(first);
-use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG
-    wxTheApp);
-use Wx::Event qw(EVT_BUTTON EVT_LEFT_DOWN EVT_MENU);
+use Wx qw(:misc :sizer :button :combobox wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxTheApp);
+use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_LEFT_DOWN EVT_MENU);
 use base 'Wx::ScrolledWindow';
 
 use constant ICON_MATERIAL      => 0;
 use constant ICON_SOLIDMESH     => 1;
 use constant ICON_MODIFIERMESH  => 2;
 
+use constant TYPE_OBJECT        => -1;
+use constant TYPE_PART          => 0;
+use constant TYPE_MODIFIER      => 1;
+use constant TYPE_SUPPORT_ENFORCER => 2;
+use constant TYPE_SUPPORT_BLOCKER => 3;
+
 my %icons = (
     'Advanced'              => 'wand.png',
     'Extruders'             => 'funnel.png',
@@ -36,13 +41,14 @@ sub new {
     $self->{config} = Slic3r::Config->new;
     # On change callback.
     $self->{on_change} = $params{on_change};
+    $self->{type} = TYPE_OBJECT;
     $self->{fixed_options} = {};
     
     $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
     
     $self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
     $self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0);
-    
+
     # option selector
     {
         # create the button
@@ -110,6 +116,16 @@ sub set_opt_keys {
     $self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
 }
 
+sub set_type {
+    my ($self, $type) = @_;
+    $self->{type} = $type;
+    if ($type == TYPE_SUPPORT_ENFORCER || $type == TYPE_SUPPORT_BLOCKER) {
+        $self->{btn_add}->Hide;
+    } else {
+        $self->{btn_add}->Show;
+    }
+}
+
 sub set_fixed_options {
     my ($self, $opt_keys) = @_;
     $self->{fixed_options} = { map {$_ => 1} @$opt_keys };
@@ -121,12 +137,28 @@ sub update_optgroup {
     
     $self->{options_sizer}->Clear(1);
     return if !defined $self->{config};
-    
+
+    if ($self->{type} != TYPE_OBJECT) {
+        my $label = Wx::StaticText->new($self, -1, "Type:"),
+        my $selection = [ "Part", "Modifier", "Support Enforcer", "Support Blocker" ];
+        my $field = Wx::ComboBox->new($self, -1, $selection->[$self->{type}], wxDefaultPosition, Wx::Size->new(160, -1), $selection, wxCB_READONLY);
+        my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
+        $sizer->Add($label, 1, wxEXPAND | wxALL, 5);
+        $sizer->Add($field, 0, wxALL, 5);
+        EVT_COMBOBOX($self, $field, sub {
+            my $idx = $field->GetSelection;  # get index of selected value
+            $self->{on_change}->("part_type", $idx) if $self->{on_change};
+        });
+        $self->{options_sizer}->Add($sizer, 0, wxEXPAND | wxBOTTOM, 0);
+    }
+
     my %categories = ();
-    foreach my $opt_key (@{$self->{config}->get_keys}) {
-        my $category = $Slic3r::Config::Options->{$opt_key}{category};
-        $categories{$category} ||= [];
-        push @{$categories{$category}}, $opt_key;
+    if ($self->{type} != TYPE_SUPPORT_ENFORCER && $self->{type} != TYPE_SUPPORT_BLOCKER) {
+        foreach my $opt_key (@{$self->{config}->get_keys}) {
+            my $category = $Slic3r::Config::Options->{$opt_key}{category};
+            $categories{$category} ||= [];
+            push @{$categories{$category}}, $opt_key;
+        }
     }
     foreach my $category (sort keys %categories) {
         my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
diff --git a/resources/icons/support_blocker.png b/resources/icons/support_blocker.png
new file mode 100644
index 000000000..8a66c73f4
Binary files /dev/null and b/resources/icons/support_blocker.png differ
diff --git a/resources/icons/support_enforcer.png b/resources/icons/support_enforcer.png
new file mode 100644
index 000000000..c0bef8d5e
Binary files /dev/null and b/resources/icons/support_enforcer.png differ
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 1f2a93375..83bedd754 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -288,7 +288,6 @@ add_library(libslic3r_gui STATIC
     ${LIBDIR}/slic3r/AppController.hpp
     ${LIBDIR}/slic3r/AppController.cpp
     ${LIBDIR}/slic3r/AppControllerWx.cpp
-    ${LIBDIR}/slic3r/Strings.hpp
 )
 
 add_library(admesh STATIC
@@ -565,7 +564,7 @@ if (WIN32)
 endif ()
 
 # SLIC3R_MSVC_PDB
-if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release")
+if (MSVC AND SLIC3R_MSVC_PDB AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release")
     set_target_properties(XS PROPERTIES
         COMPILE_FLAGS "/Zi"
         LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 50f424e6d..2b2893c80 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1225,8 +1225,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
 	return true;
 }
 
-Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
+Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
 {
+	assert(std::abs(2 * offset) < m_resolution);
+
 	typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
 	// 0) Prepare a binary grid.
 	size_t cell_rows = m_rows + 2;
@@ -1237,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
 			cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
 	// Fill in empty cells, which have a left / right neighbor filled.
 	// Fill in empty cells, which have the top / bottom neighbor filled.
-	{
+	if (fill_holes) {
 		std::vector<char> cell_inside2(cell_inside);
 		for (int r = 1; r + 1 < int(cell_rows); ++ r) {
 			for (int c = 1; c + 1 < int(cell_cols); ++ c) {
diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp
index 3eb741865..ab1aa4ed0 100644
--- a/xs/src/libslic3r/EdgeGrid.hpp
+++ b/xs/src/libslic3r/EdgeGrid.hpp
@@ -58,7 +58,7 @@ public:
 	const size_t		cols() const { return m_cols; }
 
 	// For supports: Contours enclosing the rasterized edges.
-	Polygons 			contours_simplified(coord_t offset) const;
+	Polygons 			contours_simplified(coord_t offset, bool fill_holes) const;
 
 protected:
 	struct Cell {
diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp
index e52498ecb..6933544b6 100644
--- a/xs/src/libslic3r/ExPolygonCollection.cpp
+++ b/xs/src/libslic3r/ExPolygonCollection.cpp
@@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point &center)
 }
 
 template <class T>
-bool
-ExPolygonCollection::contains(const T &item) const
+bool ExPolygonCollection::contains(const T &item) const
 {
-    for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
-        if (it->contains(item)) return true;
-    }
+    for (const ExPolygon &poly : this->expolygons)
+        if (poly.contains(item))
+            return true;
     return false;
 }
 template bool ExPolygonCollection::contains<Point>(const Point &item) const;
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 90675c04a..d5777349d 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -91,6 +91,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     virtual double min_mm3_per_mm() const = 0;
     virtual Polyline as_polyline() const = 0;
+    virtual void   collect_polylines(Polylines &dst) const = 0;
+    virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
     virtual double length() const = 0;
     virtual double total_volume() const = 0;
 };
@@ -123,8 +125,11 @@ public:
 
     ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
     void reverse() { this->polyline.reverse(); }
-    Point first_point() const { return this->polyline.points.front(); }
-    Point last_point() const { return this->polyline.points.back(); }
+    Point first_point() const override { return this->polyline.points.front(); }
+    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(); }
     // Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
     // Currently not used.
     void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
@@ -133,8 +138,8 @@ public:
     void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
     void clip_end(double distance);
     void simplify(double tolerance);
-    virtual double length() const;
-    virtual ExtrusionRole role() const { return m_role; }
+    double length() const override;
+    ExtrusionRole role() const override { return m_role; }
     // 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;
@@ -149,7 +154,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const { return this->mm3_per_mm; }
     Polyline as_polyline() const { return this->polyline; }
-    virtual double total_volume() const { return mm3_per_mm * unscale<double>(length()); }
+    void   collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
+    double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
 
 private:
     void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -178,10 +184,10 @@ public:
     bool can_reverse() const { return true; }
     ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
     void reverse();
-    Point first_point() const { return this->paths.front().polyline.points.front(); }
-    Point last_point() const { return this->paths.back().polyline.points.back(); }
-    virtual double length() const;
-    virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
+    Point first_point() const override { return this->paths.front().polyline.points.front(); }
+    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.
     // 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;
@@ -196,7 +202,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const;
     Polyline as_polyline() const;
-    virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+    void   collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+    double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
 };
 
 // Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -218,18 +225,18 @@ public:
     bool make_clockwise();
     bool make_counter_clockwise();
     void reverse();
-    Point first_point() const { return this->paths.front().polyline.points.front(); }
-    Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
+    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(); }
     Polygon polygon() const;
-    virtual double length() const;
+    double length() const override;
     bool split_at_vertex(const Point &point);
     void split_at(const Point &point, bool prefer_non_overhang);
     void clip_end(double distance, ExtrusionPaths* paths) const;
     // Test, whether the point is extruded by a bridging flow.
     // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
     bool has_overhang_point(const Point &point) const;
-    virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
-    ExtrusionLoopRole     loop_role() const { return m_loop_role; }
+    ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
+    ExtrusionLoopRole loop_role() const { return m_loop_role; }
     // 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;
@@ -244,7 +251,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const;
     Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
-    virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+    void   collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+    double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
 
 private:
     ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 3b34145f8..ee0d3d5cd 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -24,7 +24,7 @@ public:
     explicit operator ExtrusionPaths() const;
     
     bool is_collection() const { return true; };
-    virtual ExtrusionRole role() const {
+    ExtrusionRole role() const override {
         ExtrusionRole out = erNone;
         for (const ExtrusionEntity *ee : entities) {
             ExtrusionRole er = ee->role();
@@ -71,11 +71,11 @@ public:
     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.
-    virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
+    void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
-    virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
+    void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
     Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
         { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
@@ -84,14 +84,20 @@ public:
     void flatten(ExtrusionEntityCollection* retval) const;
     ExtrusionEntityCollection flatten() const;
     double min_mm3_per_mm() const;
-    virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
+    double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
 
     // Following methods shall never be called on an ExtrusionEntityCollection.
     Polyline as_polyline() const {
         CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
         return Polyline();
     };
-    virtual double length() const {
+
+    void collect_polylines(Polylines &dst) const override {
+        for (ExtrusionEntity* extrusion_entity : this->entities)
+            extrusion_entity->collect_polylines(dst);
+    }
+
+    double length() const override {
         CONFESS("Calling length() on a ExtrusionEntityCollection");
         return 0.;        
     }
diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
index 6f26167a2..cbfe926f2 100644
--- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
         Polylines paths;
         {
             Polylines p;
-            for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
-                p.push_back((Polyline)(*it));
+            for (Polygon &poly : polygons)
+                p.emplace_back(poly.points);
             paths = intersection_pl(p, to_polygons(expolygon));
         }
 
diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp
index b60e26dcc..e92674a17 100644
--- a/xs/src/libslic3r/Flow.cpp
+++ b/xs/src/libslic3r/Flow.cpp
@@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
         // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
         float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
         (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
-        false);
+        // bridge_flow_ratio
+        0.f);
 }
 
 Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
@@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
         (width.value > 0) ? width : object->config.extrusion_width,
         float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
         (layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
-        false);
+        // bridge_flow_ratio
+        0.f);
 }
 
 Flow support_material_interface_flow(const PrintObject *object, float layer_height)
@@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
         // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
         float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
         (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
-        false);
+        // bridge_flow_ratio
+        0.f);
 }
 
 }
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index c5c37a128..43c99f19f 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
 
 const char* NAME_KEY = "name";
 const char* MODIFIER_KEY = "modifier";
+const char* VOLUME_TYPE_KEY = "volume_type";
 
 const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
 const char* VALID_OBJECT_TYPES[] =
@@ -1442,7 +1443,9 @@ namespace Slic3r {
                 if (metadata.key == NAME_KEY)
                     volume->name = metadata.value;
                 else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
-                    volume->modifier = true;
+                    volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+                else if (metadata.key == VOLUME_TYPE_KEY)
+                    volume->set_type(ModelVolume::type_from_string(metadata.value));
                 else
                     volume->config.set_deserialize(metadata.key, metadata.value);
             }
@@ -1957,9 +1960,12 @@ namespace Slic3r {
                             if (!volume->name.empty())
                                 stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
 
-                            // stores volume's modifier field
-                            if (volume->modifier)
+                            // stores volume's modifier field (legacy, to support old slicers)
+                            if (volume->is_modifier())
                                 stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+                            // stores volume's type (overrides the modifier field above)
+                            stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " << 
+                                VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
 
                             // stores volume's config data
                             for (const std::string& key : volume->config.keys())
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 3f3839d43..803d9ee54 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -495,9 +495,14 @@ void AMFParserContext::endElement(const char * /* name */)
 					p = end + 1;
                 }
                 m_object->layer_height_profile_valid = true;
-            } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
-                // Is this volume a modifier volume?
-                m_volume->modifier = atoi(m_value[1].c_str()) == 1;
+            } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
+                if (strcmp(opt_key, "modifier") == 0) {
+                    // Is this volume a modifier volume?
+                    // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
+                    m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+                } else if (strcmp(opt_key, "volume_type") == 0) {
+                    m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
+                }
             }
         } else if (m_path.size() == 3) {
             if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -822,8 +827,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
                 stream << "        <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
             if (!volume->name.empty())
                 stream << "        <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
-            if (volume->modifier)
+            if (volume->is_modifier())
                 stream << "        <metadata type=\"slic3r.modifier\">1</metadata>\n";
+            stream << "        <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
             for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
                 stream << "        <triangle>\n";
                 for (int j = 0; j < 3; ++j)
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 74a376ade..eb80a588c 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -276,7 +276,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
 }
 
 
-
 std::string WipeTowerIntegration::prime(GCode &gcodegen)
 {
     assert(m_layer_idx == 0);
@@ -967,17 +966,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 
     // Get filament stats.
     print.filament_stats.clear();
-    print.total_used_filament    = 0.;
-    print.total_extruded_volume  = 0.;
-    print.total_weight           = 0.;
-    print.total_cost             = 0.;
+    print.total_used_filament       = 0.;
+    print.total_extruded_volume     = 0.;
+    print.total_weight              = 0.;
+    print.total_cost                = 0.;
+    print.total_wipe_tower_cost     = 0.;
+    print.total_wipe_tower_filament = 0.;
     print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
     print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
     for (const Extruder &extruder : m_writer.extruders()) {
-        double used_filament   = extruder.used_filament();
-        double extruded_volume = extruder.extruded_volume();
+        double used_filament   = extruder.used_filament() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f);
+        double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
         double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
         double filament_cost   = filament_weight * extruder.filament_cost()    * 0.001;
+
         print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
         _write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
         if (filament_weight > 0.) {
@@ -988,8 +990,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
                 _write_format(file, "; filament cost = %.1lf\n", filament_cost);
             }
         }
-        print.total_used_filament = print.total_used_filament + used_filament;
-        print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
+        print.total_used_filament += used_filament;
+        print.total_extruded_volume += extruded_volume;
+        print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
+        print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
     }
     _write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
     _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index dc8a32135..e16c3213d 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -98,6 +98,7 @@ public:
     void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
     std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
     std::string finalize(GCode &gcodegen);
+    std::vector<float> used_filament_length() const;
 
 private:
     WipeTowerIntegration& operator=(const WipeTowerIntegration&);
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index c35a0feb3..5cbbc1ca9 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -154,6 +154,12 @@ public:
 	// the wipe tower has been completely covered by the tool change extrusions,
 	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
 	virtual bool 		     layer_finished() const = 0;
+
+    // Returns used filament length per extruder:
+    virtual std::vector<float> get_used_filament() const = 0;
+
+    // Returns total number of toolchanges:
+    virtual int get_number_of_toolchanges() const = 0;
 };
 
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 42c06252b..61d8df035 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -111,9 +111,10 @@ public:
 	const WipeTower::xy	 start_pos_rotated() const { return m_start_pos; }
 	const WipeTower::xy  pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
+    float                get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
 
 	// Extrude with an explicitely provided amount of extrusion.
-	Writer& extrude_explicit(float x, float y, float e, float f = 0.f) 
+	Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
 	{
 		if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
 			// Neither extrusion nor a travel move.
@@ -122,6 +123,8 @@ public:
 		float dx = x - m_current_pos.x;
 		float dy = y - m_current_pos.y;
 		double len = sqrt(dx*dx+dy*dy);
+        if (record_length)
+            m_used_filament_length += e;
 
 
 		// Now do the "internal rotation" with respect to the wipe tower center
@@ -162,8 +165,8 @@ public:
 		return *this;
 	}
 
-	Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f) 
-		{ return extrude_explicit(dest.x, dest.y, e, f); }
+	Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
+		{ return extrude_explicit(dest.x, dest.y, e, f, record_length); }
 
 	// Travel to a new XY position. f=0 means use the current value.
 	Writer& travel(float x, float y, float f = 0.f)
@@ -177,7 +180,7 @@ public:
 	{
 		float dx = x - m_current_pos.x;
 		float dy = y - m_current_pos.y;
-		return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f);
+		return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
 	}
 
 	Writer& extrude(const WipeTower::xy &dest, const float f = 0.f) 
@@ -259,8 +262,8 @@ public:
 	// extrude quickly amount e to x2 with feed f.
 	Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
 	{
-		extrude_explicit(x1, m_current_pos.y + dy, e0, f);
-		extrude_explicit(x2, m_current_pos.y, e);
+		extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
+		extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
 		return *this;
 	}
 
@@ -404,6 +407,7 @@ private:
 	float		  m_last_fan_speed = 0.f;
     int           current_temp = -1;
     const float   m_default_analyzer_line_width;
+    float         m_used_filament_length = 0.f;
 
 	std::string   set_format_X(float x)
 	{
@@ -525,6 +529,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
         ++ m_num_tool_changes;
     }
 
+    m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
+                            // in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
+
 	// Reset the extruder current to a normal value.
 	writer.set_extruder_trimpot(550)
 		  .feedrate(6000)
@@ -537,6 +544,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	// so that tool_change() will know to extrude the wipe tower brim:
 	m_print_brim = true;
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = true;
 	result.print_z 	  	= this->m_z_pos;
@@ -606,10 +616,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
         toolchange_Load(writer, cleaning_box);
         writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
         toolchange_Wipe(writer, cleaning_box, wipe_volume);     // Wipe the newly loaded filament until the end of the assigned wipe area.
+        ++ m_num_tool_changes;
     } else
         toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
-    ++ m_num_tool_changes;
     m_depth_traversed += wipe_area;
 
     if (last_change_in_layer) {// draw perimeter line
@@ -632,6 +642,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
                   ";------------------\n"
                   "\n\n");
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = false;
 	result.print_z 	  	= this->m_z_pos;
@@ -683,6 +696,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
 
     m_print_brim = false;  // Mark the brim as extruded
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = false;
 	result.print_z 	  	= this->m_z_pos;
@@ -804,8 +820,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
           .load_move_x_advanced(old_x,         -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
           .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
           .resume_preview();
-
-    if (new_temperature != 0 && new_temperature != m_old_temperature ) { 	// Set the extruder temperature, but don't wait.
+    if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { 	// Set the extruder temperature, but don't wait.
+        // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
+        // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
 		writer.set_extruder_temp(new_temperature, false);
         m_old_temperature = new_temperature;
     }
@@ -849,6 +866,9 @@ void WipeTowerPrusaMM::toolchange_Change(
 	const unsigned int 	new_tool, 
 	material_type 		new_material)
 {
+    // Ask the writer about how much of the old filament we consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	// Speed override for the material. Go slow for flex and soluble materials.
 	int speed_override;
 	switch (new_material) {
@@ -911,7 +931,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	const float& xl = cleaning_box.ld.x;
 	const float& xr = cleaning_box.rd.x;
 
-
 	// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
     //   the ordered volume, even if it means violating the box. This can later be removed and simply
     // wipe until the end of the assigned area.
@@ -926,7 +945,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
         m_left_to_right = !m_left_to_right;
     }
     
-
     // now the wiping itself:
 	for (int i = 0; true; ++i)	{
 		if (i!=0) {
@@ -935,7 +953,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 			else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
 			else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
 		}
-		
+
 		float traversed_x = writer.x();
 		if (m_left_to_right)
 			writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
@@ -1050,6 +1068,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 
     m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = false;
 	result.print_z 	  	= this->m_z_pos;
@@ -1167,6 +1188,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 
     m_layer_info = m_plan.begin();
     m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
+    for (auto& used : m_used_filament_length) // reset used filament stats
+        used = 0.f;
 
     std::vector<WipeTower::ToolChangeResult> layer_result;
 	for (auto layer : m_plan)
@@ -1208,9 +1231,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 	}
 }
 
-
-
-
 void WipeTowerPrusaMM::make_wipe_tower_square()
 {
 	const float width = m_wipe_tower_width - 3 * m_perimeter_width;
@@ -1234,7 +1254,6 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
 	plan_tower();				// propagates depth downwards again (width has changed)
 	for (auto& lay : m_plan)	// depths set, now the spacing
 		lay.extra_spacing = lay.depth / lay.toolchanges_depth();
-
 }
 
 
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 305dbc40a..06625d189 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -46,7 +46,7 @@ public:
 	WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
                      float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
                      const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
-		m_wipe_tower_pos(x, y),
+    m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
 		m_wipe_tower_rotation_angle(rotation_angle),
 		m_y_shift(0.f),
@@ -94,6 +94,8 @@ public:
         m_filpar[idx].ramming_step_multiplicator /= 100;
         while (stream >> speed)
             m_filpar[idx].ramming_speed.push_back(speed);
+
+        m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
 	}
 
 
@@ -172,6 +174,9 @@ public:
 		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
 	}
 
+    virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
+    virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
+
 
 private:
 	WipeTowerPrusaMM();
@@ -331,6 +336,9 @@ private:
 	std::vector<WipeTowerInfo> m_plan; 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
 	std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
 
+    // Stores information about used filament length per extruder:
+    std::vector<float> m_used_filament_length;
+
 
 	// Returns gcode for wipe tower brim
 	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower
diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp
index f3b460443..620583128 100644
--- a/xs/src/libslic3r/Layer.hpp
+++ b/xs/src/libslic3r/Layer.hpp
@@ -21,45 +21,37 @@ class LayerRegion
     friend class Layer;
 
 public:
-    Layer* layer() { return this->_layer; }
-    const Layer* layer() const { return this->_layer; }
-    PrintRegion* region() { return this->_region; }
-    const PrintRegion* region() const { return this->_region; }
+    Layer*              layer()        { return this->_layer; }
+    const Layer*        layer()  const { return this->_layer; }
+    PrintRegion*        region()       { return this->_region; }
+    const PrintRegion*  region() const { return this->_region; }
 
-    // collection of surfaces generated by slicing the original geometry
-    // divided by type top/bottom/internal
-    SurfaceCollection slices;
-
-    // collection of extrusion paths/loops filling gaps
-    // These fills are generated by the perimeter generator.
-    // They are not printed on their own, but they are copied to this->fills during infill generation.
-    ExtrusionEntityCollection thin_fills;
+    // Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal.
+    SurfaceCollection           slices;
 
     // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
     // and for re-starting of infills.
-    ExPolygons          fill_expolygons;
+    ExPolygons                  fill_expolygons;
     // collection of surfaces for infill generation
-    SurfaceCollection   fill_surfaces;
+    SurfaceCollection           fill_surfaces;
+    // Collection of extrusion paths/loops filling gaps.
+    // These fills are generated by the perimeter generator.
+    // They are not printed on their own, but they are copied to this->fills during infill generation.
+    ExtrusionEntityCollection   thin_fills;
 
-    // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
-    // While not necessary, the memory consumption is meager and it speeds up calculation.
-    // The perimeter_surfaces keep the IDs of the slices (top/bottom/)
-    SurfaceCollection perimeter_surfaces;
-
-    // collection of expolygons representing the bridged areas (thus not
-    // needing support material)
-    Polygons bridged;
+    // Collection of expolygons representing the bridged areas (thus not needing support material).
+    //FIXME Not used as of now.
+    Polygons                    bridged;
 
     // collection of polylines representing the unsupported bridge edges
-    PolylineCollection unsupported_bridge_edges;
+    PolylineCollection          unsupported_bridge_edges;
 
-    // ordered collection of extrusion paths/loops to build all perimeters
-    // (this collection contains only ExtrusionEntityCollection objects)
-    ExtrusionEntityCollection perimeters;
-
-    // ordered collection of extrusion paths to fill surfaces
-    // (this collection contains only ExtrusionEntityCollection objects)
-    ExtrusionEntityCollection fills;
+    // Ordered collection of extrusion paths/loops to build all perimeters.
+    // This collection contains only ExtrusionEntityCollection objects.
+    ExtrusionEntityCollection   perimeters;
+    // Ordered collection of extrusion paths to fill surfaces.
+    // This collection contains only ExtrusionEntityCollection objects.
+    ExtrusionEntityCollection   fills;
     
     Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
     void slices_to_fill_surfaces_clipped();
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index afdc9c5a2..06e7fccee 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -15,8 +15,7 @@
 
 namespace Slic3r {
 
-Flow
-LayerRegion::flow(FlowRole role, bool bridge, double width) const
+Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
 {
     return this->_region->flow(
         role,
@@ -51,8 +50,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
     }
 }
 
-void
-LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
+void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
 {
     this->perimeters.clear();
     this->thin_fills.clear();
@@ -340,8 +338,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 }
 
-void
-LayerRegion::prepare_fill_surfaces()
+void LayerRegion::prepare_fill_surfaces()
 {
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
     export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial");
@@ -382,8 +379,7 @@ LayerRegion::prepare_fill_surfaces()
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 }
 
-double
-LayerRegion::infill_area_threshold() const
+double LayerRegion::infill_area_threshold() const
 {
     double ss = this->flow(frSolidInfill).scaled_spacing();
     return ss*ss;
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 1088c2e87..9f66efaef 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -627,7 +627,8 @@ const BoundingBoxf3& ModelObject::bounding_box() const
     if (! m_bounding_box_valid) {
         BoundingBoxf3 raw_bbox;
         for (const ModelVolume *v : this->volumes)
-            if (! v->modifier)
+            if (v->is_model_part())
+                // mesh.bounding_box() returns a cached value.
                 raw_bbox.merge(v->mesh.bounding_box());
         BoundingBoxf3 bb;
         for (const ModelInstance *i : this->instances)
@@ -658,7 +659,7 @@ TriangleMesh ModelObject::raw_mesh() const
 {
     TriangleMesh mesh;
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
             mesh.merge(v->mesh);
     return mesh;
 }
@@ -669,7 +670,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
 {
     BoundingBoxf3 bb;
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier) {
+        if (v->is_model_part()) {
             if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
             bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
         }
@@ -681,7 +682,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
 {
     BoundingBoxf3 bb;
     for (ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
             bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
     return bb;
 }
@@ -692,7 +693,7 @@ void ModelObject::center_around_origin()
     // center this object around the origin
 	BoundingBoxf3 bb;
 	for (ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
 			bb.merge(v->mesh.bounding_box());
     
     // Shift is the vector from the center of the bottom face of the bounding box to the origin
@@ -798,7 +799,7 @@ size_t ModelObject::facets_count() const
 {
     size_t num = 0;
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
             num += v->mesh.stl.stats.number_of_facets;
     return num;
 }
@@ -806,7 +807,7 @@ size_t ModelObject::facets_count() const
 bool ModelObject::needed_repair() const
 {
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier && v->mesh.needed_repair())
+        if (v->is_model_part() && v->mesh.needed_repair())
             return true;
     return false;
 }
@@ -822,7 +823,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
     lower->input_file = "";
     
     for (ModelVolume *volume : this->volumes) {
-        if (volume->modifier) {
+        if (! volume->is_model_part()) {
             // don't cut modifiers
             upper->add_volume(*volume);
             lower->add_volume(*volume);
@@ -874,7 +875,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
         ModelVolume* new_volume = new_object->add_volume(*mesh);
         new_volume->name        = volume->name;
         new_volume->config      = volume->config;
-        new_volume->modifier    = volume->modifier;
+        new_volume->set_type(volume->type());
         new_volume->material_id(volume->material_id());
         
         new_objects->push_back(new_object);
@@ -888,7 +889,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
 {
     for (const ModelVolume* vol : this->volumes)
     {
-        if (!vol->modifier)
+        if (vol->is_model_part())
         {
             for (ModelInstance* inst : this->instances)
             {
@@ -985,6 +986,37 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
     return m_convex_hull;
 }
 
+ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
+{
+    // Legacy support
+    if (s == "0")
+        return MODEL_PART;
+    if (s == "1")
+        return PARAMETER_MODIFIER;
+    // New type (supporting the support enforcers & blockers)
+    if (s == "ModelPart")
+        return MODEL_PART;
+    if (s == "ParameterModifier")
+        return PARAMETER_MODIFIER;
+    if (s == "SupportEnforcer")
+        return SUPPORT_ENFORCER;
+    if (s == "SupportBlocker")
+        return SUPPORT_BLOCKER;
+}
+
+std::string ModelVolume::type_to_string(const Type t)
+{
+    switch (t) {
+    case MODEL_PART:         return "ModelPart";
+    case PARAMETER_MODIFIER: return "ParameterModifier";
+    case SUPPORT_ENFORCER:   return "SupportEnforcer";
+    case SUPPORT_BLOCKER:    return "SupportBlocker";
+    default:
+        assert(false);
+        return "ModelPart";
+    }
+}
+
 // Split this volume, append the result to the object owning this volume.
 // Return the number of volumes created from this one.
 // This is useful to assign different materials to different volumes of an object.
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index da0628fd0..e9b13b23d 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -167,15 +167,27 @@ public:
     // Configuration parameters specific to an object model geometry or a modifier volume, 
     // overriding the global Slic3r settings and the ModelObject settings.
     DynamicPrintConfig config;
-    // Is it an object to be printed, or a modifier volume?
-    bool modifier;
-    
+
+    enum Type {
+        MODEL_TYPE_INVALID = -1,
+        MODEL_PART = 0,
+        PARAMETER_MODIFIER,
+        SUPPORT_ENFORCER,
+        SUPPORT_BLOCKER,
+    };
+
     // A parent object owning this modifier volume.
-    ModelObject* get_object() const { return this->object; };
+    ModelObject*        get_object() const { return this->object; };
+    Type                type() const { return m_type; }
+    void                set_type(const Type t) { m_type = t; }
+    bool                is_model_part()         const { return m_type == MODEL_PART; }
+    bool                is_modifier()           const { return m_type == PARAMETER_MODIFIER; }
+    bool                is_support_enforcer()   const { return m_type == SUPPORT_ENFORCER; }
+    bool                is_support_blocker()    const { return m_type == SUPPORT_BLOCKER; }
     t_model_material_id material_id() const { return this->_material_id; }
-    void material_id(t_model_material_id material_id);
-    ModelMaterial* material() const;
-    void set_material(t_model_material_id material_id, const ModelMaterial &material);
+    void                material_id(t_model_material_id material_id);
+    ModelMaterial*      material() const;
+    void                set_material(t_model_material_id material_id, const ModelMaterial &material);
     // Split this volume, append the result to the object owning this volume.
     // Return the number of volumes created from this one.
     // This is useful to assign different materials to different volumes of an object.
@@ -186,24 +198,30 @@ public:
     void calculate_convex_hull();
     const TriangleMesh& get_convex_hull() const;
 
+    // Helpers for loading / storing into AMF / 3MF files.
+    static Type         type_from_string(const std::string &s);
+    static std::string  type_to_string(const Type t);
+
 private:
     // Parent object owning this ModelVolume.
-    ModelObject* object;
-    t_model_material_id _material_id;
+    ModelObject*            object;
+    // Is it an object to be printed, or a modifier volume?
+    Type                    m_type;
+    t_model_material_id     _material_id;
     
-    ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
+    ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
     {
         if (mesh.stl.stats.number_of_facets > 1)
             calculate_convex_hull();
     }
-    ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
+    ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
     ModelVolume(ModelObject *object, const ModelVolume &other) :
-        name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
+        name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
     {
         this->material_id(other.material_id());
     }
     ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
-        name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
+        name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
     {
         this->material_id(other.material_id());
         if (mesh.stl.stats.number_of_facets > 1)
diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp
index 3c2c2aa29..372689c39 100644
--- a/xs/src/libslic3r/ModelArrange.hpp
+++ b/xs/src/libslic3r/ModelArrange.hpp
@@ -130,6 +130,7 @@ objfunc(const PointImpl& bincenter,
         double norm,            // A norming factor for physical dimensions
         // a spatial index to quickly get neighbors of the candidate item
         const SpatIndex& spatindex,
+        const SpatIndex& smalls_spatindex,
         const ItemGroup& remaining
         )
 {
@@ -161,7 +162,7 @@ objfunc(const PointImpl& bincenter,
     // Will hold the resulting score
     double score = 0;
 
-    if(isBig(item.area())) {
+    if(isBig(item.area()) || spatindex.empty()) {
         // This branch is for the bigger items..
 
         auto minc = ibb.minCorner(); // bottom left corner
@@ -183,6 +184,8 @@ objfunc(const PointImpl& bincenter,
 
         // The smalles distance from the arranged pile center:
         auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
+        auto bindist = pl::distance(ibb.center(), bincenter) / norm;
+        dist = 0.8*dist + 0.2*bindist;
 
         // Density is the pack density: how big is the arranged pile
         double density = 0;
@@ -207,14 +210,20 @@ objfunc(const PointImpl& bincenter,
             // candidate to be aligned with only one item.
             auto alignment_score = 1.0;
 
-            density = (fullbb.width()*fullbb.height()) / (norm*norm);
+            density = std::sqrt((fullbb.width() / norm )*
+                                (fullbb.height() / norm));
             auto querybb = item.boundingBox();
 
             // Query the spatial index for the neighbors
             std::vector<SpatElement> result;
             result.reserve(spatindex.size());
-            spatindex.query(bgi::intersects(querybb),
-                            std::back_inserter(result));
+            if(isBig(item.area())) {
+                spatindex.query(bgi::intersects(querybb),
+                                std::back_inserter(result));
+            } else {
+                smalls_spatindex.query(bgi::intersects(querybb),
+                                       std::back_inserter(result));
+            }
 
             for(auto& e : result) { // now get the score for the best alignment
                 auto idx = e.second;
@@ -235,12 +244,8 @@ objfunc(const PointImpl& bincenter,
             if(result.empty())
                 score = 0.5 * dist + 0.5 * density;
             else
-                score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
+                score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score;
         }
-    } else if( !isBig(item.area()) && spatindex.empty()) {
-        auto bindist = pl::distance(ibb.center(), bincenter) / norm;
-        // Bindist is surprisingly enough...
-        score = bindist;
     } else {
         // Here there are the small items that should be placed around the
         // already processed bigger items.
@@ -291,6 +296,7 @@ protected:
     PConfig pconf_; // Placement configuration
     double bin_area_;
     SpatIndex rtree_;
+    SpatIndex smallsrtree_;
     double norm_;
     Pile merged_pile_;
     Box pilebb_;
@@ -318,6 +324,7 @@ public:
             pilebb_ = sl::boundingBox(merged_pile);
 
             rtree_.clear();
+            smallsrtree_.clear();
 
             // We will treat big items (compared to the print bed) differently
             auto isBig = [this](double a) {
@@ -327,6 +334,7 @@ public:
             for(unsigned idx = 0; idx < items.size(); ++idx) {
                 Item& itm = items[idx];
                 if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
+                smallsrtree_.insert({itm.boundingBox(), idx});
             }
         };
 
@@ -360,6 +368,7 @@ public:
                                   bin_area_,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
 
             double score = std::get<0>(result);
@@ -397,6 +406,7 @@ public:
                                   bin_area_,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
 
             double score = std::get<0>(result);
@@ -440,6 +450,7 @@ public:
                                   bin_area_,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
             double score = std::get<0>(result);
 
@@ -468,6 +479,7 @@ public:
                                   0,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
             return std::get<0>(result);
         };
diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp
index 1fef4083b..03b89df51 100644
--- a/xs/src/libslic3r/MultiPoint.hpp
+++ b/xs/src/libslic3r/MultiPoint.hpp
@@ -35,8 +35,10 @@ public:
     Point first_point() const;
     virtual Point last_point() const = 0;
     virtual Lines lines() const = 0;
+    size_t size() const { return points.size(); }
+    bool   empty() const { return points.empty(); }
     double length() const;
-    bool is_valid() const { return this->points.size() >= 2; }
+    bool   is_valid() const { return this->points.size() >= 2; }
 
     int  find_point(const Point &point) const;
     bool has_boundary_point(const Point &point) const;
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index c1aff561f..6d9d82d25 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -22,6 +22,7 @@ typedef Point Vector;
 // Vector types with a fixed point coordinate base type.
 typedef Eigen::Matrix<coord_t,  2, 1, Eigen::DontAlign> Vec2crd;
 typedef Eigen::Matrix<coord_t,  3, 1, Eigen::DontAlign> Vec3crd;
+typedef Eigen::Matrix<int,      3, 1, Eigen::DontAlign> Vec3i;
 typedef Eigen::Matrix<int64_t,  2, 1, Eigen::DontAlign> Vec2i64;
 typedef Eigen::Matrix<int64_t,  3, 1, Eigen::DontAlign> Vec3i64;
 
diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 54909352c..63162d953 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
         p.rotate(cos_angle, sin_angle);
 }
 
+inline void polygons_reverse(Polygons &polys)
+{
+    for (Polygon &p : polys)
+        p.reverse();
+}
+
 inline Points to_points(const Polygon &poly)
 {
     return poly.points;
diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp
index b2e50bca3..e0cd5221c 100644
--- a/xs/src/libslic3r/Polyline.cpp
+++ b/xs/src/libslic3r/Polyline.cpp
@@ -184,15 +184,13 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
 
 bool Polyline::is_straight() const
 {
-    /*  Check that each segment's direction is equal to the line connecting
-        first point and last point. (Checking each line against the previous
-        one would cause the error to accumulate.) */
+    // Check that each segment's direction is equal to the line connecting
+    // first point and last point. (Checking each line against the previous
+    // one would cause the error to accumulate.)
     double dir = Line(this->first_point(), this->last_point()).direction();
-    
-    Lines lines = this->lines();
-    for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
-        if (!line->parallel_to(dir)) return false;
-    }
+    for (const auto &line: this->lines())
+        if (! line.parallel_to(dir))
+            return false;
     return true;
 }
 
diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp
index 0c934e074..925b88aca 100644
--- a/xs/src/libslic3r/Polyline.hpp
+++ b/xs/src/libslic3r/Polyline.hpp
@@ -21,6 +21,8 @@ public:
     Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
     Polyline(std::initializer_list<Point> list) : MultiPoint(list) {}
     explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
+    explicit Polyline(const Points &points) : MultiPoint(points) {}
+    explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
     Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
     Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
 	static Polyline new_scale(const std::vector<Vec2d> &points) {
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index aeb034378..a582a2b62 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -366,9 +366,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
     // Invalidate all print steps.
     this->invalidate_all_steps();
 
-    for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
+    size_t volume_id = 0;
+    for (const ModelVolume *volume : model_object->volumes) {
+        if (! volume->is_model_part() && ! volume->is_modifier())
+            continue;
         // Get the config applied to this volume.
-        PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
+        PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
         // Find an existing print region with the same config.
         size_t region_id = size_t(-1);
         for (size_t i = 0; i < this->regions.size(); ++ i)
@@ -383,6 +386,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
         }
         // Assign volume to a region.
         object->add_region_volume(region_id, volume_id);
+        ++ volume_id;
     }
 
     // Apply config to print object.
@@ -857,7 +861,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
     for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
         ModelVolume *volume = model_object->volumes[volume_id];
         //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
-        if (! volume->material_id().empty() && ! volume->config.has("extruder"))
+        if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
             volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
     }
 }
@@ -1197,6 +1201,9 @@ void Print::_make_wipe_tower()
     }
     m_wipe_tower_final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
 		wipe_tower.tool_change((unsigned int)-1, false));
+
+    m_wipe_tower_used_filament = wipe_tower.get_used_filament();
+    m_wipe_tower_number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
 }
 
 std::string Print::output_filename()
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 86dca2e26..7c2638c96 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -80,7 +80,10 @@ public:
 
     Print* print() { return this->_print; }
     Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
+    // Average diameter of nozzles participating on extruding this region.
     coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
+    // Average diameter of nozzles participating on extruding this region.
+    coordf_t bridging_height_avg(const PrintConfig &print_config) const;
 
 private:
     Print* _print;
@@ -211,6 +214,10 @@ public:
 
     bool is_printable() const { return !this->_shifted_copies.empty(); }
 
+    // Helpers to slice support enforcer / blocker meshes by the support generator.
+    std::vector<ExPolygons>     slice_support_enforcers() const;
+    std::vector<ExPolygons>     slice_support_blockers() const;
+
 private:
     Print* _print;
     ModelObject* _model_object;
@@ -222,6 +229,7 @@ private:
     ~PrintObject() {}
 
     std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
+    std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
 };
 
 typedef std::vector<PrintObject*> PrintObjectPtrs;
@@ -246,7 +254,7 @@ public:
 
     std::string                     estimated_normal_print_time;
     std::string                     estimated_silent_print_time;
-    double                          total_used_filament, total_extruded_volume, total_cost, total_weight;
+    double                          total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament;
     std::map<size_t, float>         filament_stats;
     PrintState<PrintStep, psCount>  state;
 
@@ -315,6 +323,8 @@ public:
     std::unique_ptr<WipeTower::ToolChangeResult>          m_wipe_tower_priming;
     std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
     std::unique_ptr<WipeTower::ToolChangeResult>          m_wipe_tower_final_purge;
+    std::vector<float>                                    m_wipe_tower_used_filament;
+    int                                                   m_wipe_tower_number_of_toolchanges = -1;
 
     std::string output_filename();
     std::string output_filepath(const std::string &path);
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index a3e84a356..94b0ad5aa 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1717,6 +1717,14 @@ void PrintConfigDef::init_fff_params()
     def->cli = "support-material!";
     def->default_value = new ConfigOptionBool(false);
 
+    def = this->add("support_material_auto", coBool);
+    def->label = L("Auto generated supports");
+    def->category = L("Support material");
+    def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
+                     " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
+    def->cli = "support-material-auto!";
+    def->default_value = new ConfigOptionBool(true);
+
     def = this->add("support_material_xy_spacing", coFloatOrPercent);
     def->label = L("XY separation between an object and its support");
     def->category = L("Support material");
@@ -1755,7 +1763,7 @@ void PrintConfigDef::init_fff_params()
                    "for the first object layer.");
     def->sidetext = L("mm");
     def->cli = "support-material-contact-distance=f";
-    def->min = 0;
+//    def->min = 0;
     def->enum_values.push_back("0");
     def->enum_values.push_back("0.2");
 	def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 5bc99a51a..bc3b0ef49 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -345,6 +345,7 @@ public:
     ConfigOptionFloatOrPercent      extrusion_width;
     ConfigOptionFloatOrPercent      first_layer_height;
     ConfigOptionBool                infill_only_where_needed;
+    // Force the generation of solid shells between adjacent materials/volumes.
     ConfigOptionBool                interface_shells;
     ConfigOptionFloat               layer_height;
     ConfigOptionInt                 raft_layers;
@@ -352,6 +353,9 @@ public:
 //    ConfigOptionFloat               seam_preferred_direction;
 //    ConfigOptionFloat               seam_preferred_direction_jitter;
     ConfigOptionBool                support_material;
+    // Automatic supports (generated based on support_material_threshold).
+    ConfigOptionBool                support_material_auto;
+    // Direction of the support pattern (in XY plane).
     ConfigOptionFloat               support_material_angle;
     ConfigOptionBool                support_material_buildplate_only;
     ConfigOptionFloat               support_material_contact_distance;
@@ -361,12 +365,15 @@ public:
     ConfigOptionBool                support_material_interface_contact_loops;
     ConfigOptionInt                 support_material_interface_extruder;
     ConfigOptionInt                 support_material_interface_layers;
+    // Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
     ConfigOptionFloat               support_material_interface_spacing;
     ConfigOptionFloatOrPercent      support_material_interface_speed;
     ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
+    // Spacing between support material lines (the hatching distance).
     ConfigOptionFloat               support_material_spacing;
     ConfigOptionFloat               support_material_speed;
     ConfigOptionBool                support_material_synchronize_layers;
+    // Overhang angle threshold.
     ConfigOptionInt                 support_material_threshold;
     ConfigOptionBool                support_material_with_sheath;
     ConfigOptionFloatOrPercent      support_material_xy_spacing;
@@ -389,6 +396,7 @@ protected:
 //        OPT_PTR(seam_preferred_direction);
 //        OPT_PTR(seam_preferred_direction_jitter);
         OPT_PTR(support_material);
+        OPT_PTR(support_material_auto);
         OPT_PTR(support_material_angle);
         OPT_PTR(support_material_buildplate_only);
         OPT_PTR(support_material_contact_distance);
@@ -436,10 +444,12 @@ public:
     ConfigOptionInt                 infill_every_layers;
     ConfigOptionFloatOrPercent      infill_overlap;
     ConfigOptionFloat               infill_speed;
+    // Detect bridging perimeters
     ConfigOptionBool                overhangs;
     ConfigOptionInt                 perimeter_extruder;
     ConfigOptionFloatOrPercent      perimeter_extrusion_width;
     ConfigOptionFloat               perimeter_speed;
+    // Total number of perimeters.
     ConfigOptionInt                 perimeters;
     ConfigOptionFloatOrPercent      small_perimeter_speed;
     ConfigOptionFloat               solid_infill_below_area;
@@ -447,6 +457,7 @@ public:
     ConfigOptionFloatOrPercent      solid_infill_extrusion_width;
     ConfigOptionInt                 solid_infill_every_layers;
     ConfigOptionFloatOrPercent      solid_infill_speed;
+    // Detect thin walls.
     ConfigOptionBool                thin_walls;
     ConfigOptionFloatOrPercent      top_infill_extrusion_width;
     ConfigOptionInt                 top_solid_layers;
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 9d1d58d6d..65da49258 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -177,6 +177,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
             steps.emplace_back(posSlice);
         } else if (
                opt_key == "support_material"
+            || opt_key == "support_material_auto"
             || opt_key == "support_material_angle"
             || opt_key == "support_material_buildplate_only"
             || opt_key == "support_material_enforce_layers"
@@ -1325,29 +1326,62 @@ end:
 
 std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
 {
-    std::vector<ExPolygons> layers;
+    std::vector<const ModelVolume*> volumes;
     if (region_id < this->region_volumes.size()) {
-        std::vector<int> &volumes = this->region_volumes[region_id];
-        if (! volumes.empty()) {
-            // Compose mesh.
-            //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
-            TriangleMesh mesh;
-            for (int volume_id : volumes) {
-                ModelVolume *volume = this->model_object()->volumes[volume_id];
-                if (volume->modifier == modifier)
-                    mesh.merge(volume->mesh);
-            }
-            if (mesh.stl.stats.number_of_facets > 0) {
-                // transform mesh
-                // we ignore the per-instance transformations currently and only 
-                // consider the first one
-                this->model_object()->instances.front()->transform_mesh(&mesh, true);
-                // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
-                mesh.translate(- unscale<float>(this->_copies_shift(0)), - unscale<float>(this->_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
-                // perform actual slicing
-                TriangleMeshSlicer mslicer(&mesh);
-                mslicer.slice(z, &layers);
-            }
+        for (int volume_id : this->region_volumes[region_id]) {
+            const ModelVolume *volume = this->model_object()->volumes[volume_id];
+            if (modifier ? volume->is_modifier() : volume->is_model_part())
+                volumes.emplace_back(volume);
+        }
+    }
+    return this->_slice_volumes(z, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+{
+    std::vector<const ModelVolume*> volumes;
+    for (const ModelVolume *volume : this->model_object()->volumes)
+        if (volume->is_support_enforcer())
+            volumes.emplace_back(volume);
+    std::vector<float> zs;
+    zs.reserve(this->layers.size());
+    for (const Layer *l : this->layers)
+        zs.emplace_back(l->slice_z);
+    return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+{
+    std::vector<const ModelVolume*> volumes;
+    for (const ModelVolume *volume : this->model_object()->volumes)
+        if (volume->is_support_blocker())
+            volumes.emplace_back(volume);
+    std::vector<float> zs;
+    zs.reserve(this->layers.size());
+    for (const Layer *l : this->layers)
+        zs.emplace_back(l->slice_z);
+    return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
+{
+    std::vector<ExPolygons> layers;
+    if (! volumes.empty()) {
+        // Compose mesh.
+        //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
+        TriangleMesh mesh;
+        for (const ModelVolume *v : volumes)
+            mesh.merge(v->mesh);
+        if (mesh.stl.stats.number_of_facets > 0) {
+            // transform mesh
+            // we ignore the per-instance transformations currently and only 
+            // consider the first one
+            this->model_object()->instances.front()->transform_mesh(&mesh, true);
+            // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
+            mesh.translate(- unscale<float>(this->_copies_shift(0)), - unscale<float>(this->_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
+            // perform actual slicing
+            TriangleMeshSlicer mslicer(&mesh);
+            mslicer.slice(z, &layers);
         }
     }
     return layers;
diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp
index 4874c71bc..5bb1fffb3 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -57,4 +57,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
             print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.;
 }
 
+coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
+{
+    return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value);
+}
+
 }
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
index 1bc38502b..b3e314549 100644
--- a/xs/src/libslic3r/Slicing.cpp
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
     // 1) Initialize the SlicingAdaptive class with the object meshes.
     SlicingAdaptive as;
     as.set_slicing_parameters(slicing_params);
-    for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
-        if (! (*it)->modifier)
-            as.add_mesh(&(*it)->mesh);
+    for (const ModelVolume *volume : volumes)
+        if (volume->is_model_part())
+            as.add_mesh(&volume->mesh);
     as.prepare();
 
     // 2) Generate layers using the algorithm of @platsch 
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 4b7e78976..1d2f330d8 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -248,10 +248,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
 #ifdef SLIC3R_DEBUG
     static int iRun = 0;
     iRun ++;
-    for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
+    for (const MyLayer *layer : top_contacts)
         Slic3r::SVG::export_expolygons(
-            debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z), 
-            union_ex((*it)->polygons, false));
+            debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), 
+            union_ex(layer->polygons, false));
 #endif /* SLIC3R_DEBUG */
 
     BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts";
@@ -282,7 +282,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
     MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers(
         object, bottom_contacts, top_contacts, layer_storage);
 
-    this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+//    this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+    this->trim_support_layers_by_object(object, top_contacts, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
+#ifdef SLIC3R_DEBUG
+    for (const MyLayer *layer : top_contacts)
+        Slic3r::SVG::export_expolygons(
+            debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), 
+            union_ex(layer->polygons, false));
+#endif
 
     BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
 
@@ -420,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
 {
     // 1) Count the new polygons first.
     size_t n_polygons_new = 0;
-    for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
-        const LayerRegion       &region = *(*it_region);
-        const SurfaceCollection &slices = region.slices;
-        for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
-            const Surface &surface = *it;
+    for (const LayerRegion *region : layer.regions)
+        for (const Surface &surface : region->slices.surfaces)
             if (surface.surface_type == surface_type)
                 n_polygons_new += surface.expolygon.holes.size() + 1;
-        }
-    }
-
     // 2) Collect the new polygons.
     Polygons out;
     out.reserve(n_polygons_new);
-    for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
-        const LayerRegion       &region = *(*it_region);
-        const SurfaceCollection &slices = region.slices;
-        for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
-            const Surface &surface = *it;
+    for (const LayerRegion *region : layer.regions)
+        for (const Surface &surface : region->slices.surfaces)
             if (surface.surface_type == surface_type)
                 polygons_append(out, surface.expolygon);
-        }
-    }
-
     return out;
 }
 
@@ -452,8 +450,8 @@ Polygons collect_slices_outer(const Layer &layer)
 {
     Polygons out;
     out.reserve(out.size() + layer.slices.expolygons.size());
-    for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it)
-        out.push_back(it->contour);
+    for (const ExPolygon &expoly : layer.slices.expolygons)
+        out.emplace_back(expoly.contour);
     return out;
 }
 
@@ -461,8 +459,11 @@ class SupportGridPattern
 {
 public:
     SupportGridPattern(
+        // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
         const Polygons &support_polygons, 
-        const Polygons &trimming_polygons, 
+        // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
+        const Polygons &trimming_polygons,
+        // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing()
         coordf_t        support_spacing, 
         coordf_t        support_angle) :
         m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons),
@@ -485,7 +486,8 @@ public:
         m_grid.set_bbox(bbox);
         m_grid.create(*m_support_polygons, grid_resolution);
         m_grid.calculate_sdf();
-        // Extract a bounding contour from the grid, trim by the object.
+        // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+        // polygons if ever these polygons get split into parts by the trimming polygons.
         m_island_samples = island_samples(*m_support_polygons);
     }
 
@@ -493,22 +495,22 @@ public:
     // and trim the extracted polygons by trimming_polygons.
     // Trimming by the trimming_polygons may split the extracted polygons into pieces.
     // Remove all the pieces, which do not contain any of the island_samples.
-    Polygons extract_support(const coord_t offset_in_grid)
+    Polygons extract_support(const coord_t offset_in_grid, bool fill_holes)
     {
         // Generate islands, so each island may be tested for overlap with m_island_samples.
+        assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
         ExPolygons islands = diff_ex(
-            m_grid.contours_simplified(offset_in_grid),
+            m_grid.contours_simplified(offset_in_grid, fill_holes),
             *m_trimming_polygons, false);
 
         // Extract polygons, which contain some of the m_island_samples.
         Polygons out;
-        std::vector<std::pair<Point,bool>> samples_inside;
-
         for (ExPolygon &island : islands) {
             BoundingBox bbox = get_extents(island.contour);
+            // Samples are sorted lexicographically.
             auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.min - Point(1, 1)));
             auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.max + Point(1, 1)));
-            samples_inside.clear();
+            std::vector<std::pair<Point,bool>> samples_inside;
             for (auto it = it_lower; it != it_upper; ++ it)
                 if (bbox.contains(*it))
                     samples_inside.push_back(std::make_pair(*it, false));
@@ -569,8 +571,10 @@ public:
 private:
     SupportGridPattern& operator=(const SupportGridPattern &rhs);
 
+#if 0
     // Get some internal point of an expolygon, to be used as a representative
     // sample to test, whether this island is inside another island.
+    //FIXME this was quick, but not sufficiently robust.
     static Point island_sample(const ExPolygon &expoly)
     {
         // Find the lowest point lexicographically.
@@ -591,7 +595,10 @@ private:
         double coef = 20. / sqrt(l2);
         return Point(p2(0) + coef * v(0), p2(1) + coef * v(1));
     }
+#endif
 
+    // Sample one internal point per expolygon.
+    // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
     static Points island_samples(const ExPolygons &expolygons)
     {
         Points pts;
@@ -629,9 +636,164 @@ private:
     coordf_t                m_support_spacing;
 
     Slic3r::EdgeGrid::Grid  m_grid;
+    // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
+    // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
     Points                  m_island_samples;
 };
 
+namespace SupportMaterialInternal {
+    static inline bool has_bridging_perimeters(const ExtrusionLoop &loop)
+    {
+        for (const ExtrusionPath &ep : loop.paths)
+            if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
+                return ep.size() >= (ep.is_closed() ? 3 : 2);
+            return false;
+    }
+    static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
+    {
+        for (const ExtrusionEntity *ee : perimeters.entities) {
+            if (ee->is_collection()) {
+                for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+                    assert(! ee2->is_collection());
+                    if (ee2->is_loop())
+                        if (has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee2)))
+                            return true;
+                }
+            } else if (ee->is_loop() && has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee)))
+                return true;
+        }
+        return false;
+    }
+    static bool has_bridging_fills(const ExtrusionEntityCollection &fills)
+    {
+        for (const ExtrusionEntity *ee : fills.entities) {
+            assert(ee->is_collection());
+            for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+                assert(! ee2->is_collection());
+                assert(! ee2->is_loop());
+                if (ee2->role() == erBridgeInfill)
+                    return true;
+            }
+        }
+        return false;
+    }
+    static bool has_bridging_extrusions(const Layer &layer) 
+    {
+        for (const LayerRegion *region : layer.regions) {
+            if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
+                return true;
+            if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
+                return true;
+        }
+        return false;
+    }
+
+    static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out)
+    {
+        assert(expansion_scaled >= 0.f);
+        for (const ExtrusionPath &ep : loop.paths)
+            if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
+                float exp = 0.5f * scale_(ep.width) + expansion_scaled;
+                if (ep.is_closed()) {
+                    if (ep.size() >= 3) {
+                        // This is a complete loop.
+                        // Add the outer contour first.
+                        Polygon poly;
+                        poly.points = ep.polyline.points;
+                        poly.points.pop_back();
+                        if (poly.area() < 0)
+                            poly.reverse();
+                        polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                        Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                        polygons_reverse(holes);
+                        polygons_append(out, holes);
+                    }
+                } else if (ep.size() >= 2) {
+                    // Offset the polyline.
+                    polygons_append(out, offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                }
+            }
+    }
+    static void collect_bridging_perimeter_areas(const ExtrusionEntityCollection &perimeters, const float expansion_scaled, Polygons &out)
+    {
+        for (const ExtrusionEntity *ee : perimeters.entities) {
+            if (ee->is_collection()) {
+                for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+                    assert(! ee2->is_collection());
+                    if (ee2->is_loop())
+                        collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee2), expansion_scaled, out);
+                }
+            } else if (ee->is_loop())
+                collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee), expansion_scaled, out);
+        }
+    }
+
+    static void remove_bridges_from_contacts(
+        const PrintConfig   &print_config, 
+        const Layer         &lower_layer,
+        const Polygons      &lower_layer_polygons,
+        LayerRegion         *layerm,
+        float                fw, 
+        Polygons            &contact_polygons)
+    {
+        // compute the area of bridging perimeters
+        Polygons bridges;
+        {
+            // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
+            Polygons lower_grown_slices = offset(lower_layer_polygons, 
+                //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
+                0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
+                SUPPORT_SURFACES_OFFSET_PARAMETERS);
+            // Collect perimeters of this layer.
+            //FIXME split_at_first_point() could split a bridge mid-way
+        #if 0
+            Polylines overhang_perimeters = layerm->perimeters.as_polylines();
+            // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
+            for (Polyline &polyline : overhang_perimeters)
+                polyline.points[0].x += 1;
+            // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
+            overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
+        #else
+            Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
+        #endif
+            
+            // only consider straight overhangs
+            // only consider overhangs having endpoints inside layer's slices
+            // convert bridging polylines into polygons by inflating them with their thickness
+            // since we're dealing with bridges, we can't assume width is larger than spacing,
+            // so we take the largest value and also apply safety offset to be ensure no gaps
+            // are left in between
+            Flow bridge_flow = layerm->flow(frPerimeter, true);
+            float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
+            for (Polyline &polyline : overhang_perimeters)
+                if (polyline.is_straight()) {
+                    // This is a bridge 
+                    polyline.extend_start(fw);
+                    polyline.extend_end(fw);
+                    // Is the straight perimeter segment supported at both sides?
+                    if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
+                        // Offset a polyline into a thick line.
+                        polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
+                }
+            bridges = union_(bridges);
+        }
+        // remove the entire bridges and only support the unsupported edges
+        //FIXME the brided regions are already collected as layerm->bridged. Use it?
+        for (const Surface &surface : layerm->fill_surfaces.surfaces)
+            if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
+                polygons_append(bridges, surface.expolygon);
+        //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
+        contact_polygons = diff(contact_polygons, bridges, true);
+        // Add the bridge anchors into the region.
+        //FIXME add supports at regular intervals to support long bridges!
+        polygons_append(contact_polygons,
+            intersection(
+                // Offset unsupported edges into polygons.
+                offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
+                bridges));
+    }
+}
+
 // Generate top contact layers supporting overhangs.
 // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
 // If supports over bed surface only are requested, don't generate contact layers over an object.
@@ -643,9 +805,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     ++ iRun; 
 #endif /* SLIC3R_DEBUG */
 
+    // Slice support enforcers / support blockers.
+    std::vector<ExPolygons> enforcers = object.slice_support_enforcers();
+    std::vector<ExPolygons> blockers  = object.slice_support_blockers();
+
     // Output layers, sorted by top Z.
     MyLayersPtr contact_out;
 
+    const bool   support_auto  = m_object_config->support_material_auto.value;
     // If user specified a custom angle threshold, convert it to radians.
     // Zero means automatic overhang detection.
     const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ? 
@@ -680,10 +847,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
     // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
     size_t num_layers = this->has_support() ? object.layer_count() : 1;
-    contact_out.assign(num_layers, nullptr);
+    // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, 
+    // and the other for the overhangs extruded with a normal flow.
+    contact_out.assign(num_layers * 2, nullptr);
     tbb::spin_mutex layer_storage_mutex;
     tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
-        [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
+        [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out]
+        (const tbb::blocked_range<size_t>& range) {
             for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) 
             {
                 const Layer &layer = *object.layers[layer_id];
@@ -694,6 +864,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                 Polygons contact_polygons;
                 Polygons slices_margin_cached;
                 float    slices_margin_cached_offset = -1.;
+                Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons);
+                // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
+                float    no_interface_offset = 0.f;
                 if (layer_id == 0) {
                     // This is the first object layer, so the object is being printed on a raft and
                     // we're here just to get the object footprint for the raft.
@@ -708,6 +881,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // Extrusion width accounts for the roundings of the extrudates.
                         // It is the maximum widh of the extrudate.
                         float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
+                        no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
                         float lower_layer_offset = 
                             (layer_id < this->m_object_config->support_material_enforce_layers.value) ? 
                                 // Enforce a full possible support, ignore the overhang angle.
@@ -720,7 +894,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // Overhang polygons for this layer and region.
                         Polygons diff_polygons;
                         Polygons layerm_polygons = to_polygons(layerm->slices);
-                        Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons);
                         if (lower_layer_offset == 0.f) {
                             // Support everything.
                             diff_polygons = diff(layerm_polygons, lower_layer_polygons);
@@ -730,28 +903,61 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                 diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
                             }
                         } else {
-                            // Get the regions needing a suport, collapse very tiny spots.
-                            //FIXME cache the lower layer offset if this layer has multiple regions.
-                            diff_polygons = offset2(
-                                diff(layerm_polygons,
-                                     offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
-                                -0.1f*fw, +0.1f*fw);
-                            if (! buildplate_covered.empty()) {
-                                // Don't support overhangs above the top surfaces.
-                                // This step is done before the contact surface is calculated by growing the overhang region.
-                                diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+                            if (support_auto) {
+                                // Get the regions needing a suport, collapse very tiny spots.
+                                //FIXME cache the lower layer offset if this layer has multiple regions.
+    #if 1
+                                diff_polygons = offset2(
+                                    diff(layerm_polygons,
+                                         offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
+                                    //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
+                                    // no support at all for not so steep overhangs.
+                                    - 0.1f * fw, 0.1f * fw);
+    #else
+                                diff_polygons = 
+                                    diff(layerm_polygons,
+                                         offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+    #endif
+                                if (! buildplate_covered.empty()) {
+                                    // Don't support overhangs above the top surfaces.
+                                    // This step is done before the contact surface is calculated by growing the overhang region.
+                                    diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+                                }
+                                if (! diff_polygons.empty()) {
+    	                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
+    	                            // This is done to increase size of the supporting columns below, as they are calculated by 
+    	                            // propagating these contact surfaces downwards.
+    	                            diff_polygons = diff(
+    	                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), 
+    	                                lower_layer_polygons);
+    							}
                             }
-                            if (diff_polygons.empty())
-                                continue;
-                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
-                            diff_polygons = diff(
-                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), 
-                                lower_layer_polygons);
+                            if (! enforcers.empty()) {
+                                // Apply the "support enforcers".
+                                //FIXME add the "enforcers" to the sparse support regions only.
+                                const ExPolygons &enforcer = enforcers[layer_id - 1];
+                                if (! enforcer.empty()) {
+                                    // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+                                    Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)),
+                                            offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                                    if (! new_contacts.empty()) {
+                                        if (diff_polygons.empty())
+                                            diff_polygons = std::move(new_contacts);
+                                        else
+                                            diff_polygons = union_(diff_polygons, new_contacts);
+                                    }
+                                }
+                            }
+                        }
+                        // Apply the "support blockers".
+                        if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) {
+                            // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+                            diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id]));
                         }
                         if (diff_polygons.empty())
                             continue;
 
-                        #ifdef SLIC3R_DEBUG                        
+                        #ifdef SLIC3R_DEBUG
                         {
                             ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", 
                                 iRun, layer_id, 
@@ -762,73 +968,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         }
                         #endif /* SLIC3R_DEBUG */
 
-                        if (this->m_object_config->dont_support_bridges) {
-                            // compute the area of bridging perimeters
-                            // Note: this is duplicate code from GCode.pm, we need to refactor
-                            if (true) {
-                                Polygons bridged_perimeters;
-                                {
-                                    Flow bridge_flow = layerm->flow(frPerimeter, true);
-                                    coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1);
-                                    Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS);
-                                    
-                                    // Collect perimeters of this layer.
-                                    // TODO: split_at_first_point() could split a bridge mid-way
-                                    Polylines overhang_perimeters;
-                                    for (ExtrusionEntity* extrusion_entity : layerm->perimeters.entities) {
-                                        const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(extrusion_entity);
-                                        assert(island != NULL);
-                                        for (size_t i = 0; i < island->entities.size(); ++ i) {
-                                            ExtrusionEntity *entity = island->entities[i];
-                                            ExtrusionLoop *loop = dynamic_cast<Slic3r::ExtrusionLoop*>(entity);
-                                            overhang_perimeters.push_back(loop ? 
-                                                loop->as_polyline() :
-                                                dynamic_cast<const Slic3r::ExtrusionPath*>(entity)->polyline);
-                                        }
-                                    }
-                                    
-                                    // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
-                                    for (Polyline &polyline : overhang_perimeters)
-                                        polyline.points[0](0) += 1;
-                                    // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
-                                    overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
-                                    
-                                    // only consider straight overhangs
-                                    // only consider overhangs having endpoints inside layer's slices
-                                    // convert bridging polylines into polygons by inflating them with their thickness
-                                    // since we're dealing with bridges, we can't assume width is larger than spacing,
-                                    // so we take the largest value and also apply safety offset to be ensure no gaps
-                                    // are left in between
-                                    float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
-                                    for (Polyline &polyline : overhang_perimeters)
-                                        if (polyline.is_straight()) {
-                                            // This is a bridge 
-                                            polyline.extend_start(fw);
-                                            polyline.extend_end(fw);
-                                            // Is the straight perimeter segment supported at both sides?
-                                            if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point()))
-                                                // Offset a polyline into a thick line.
-                                                polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f));
-                                        }
-                                    bridged_perimeters = union_(bridged_perimeters);
-                                }
-                                // remove the entire bridges and only support the unsupported edges
-                                Polygons bridges;
-                                for (const Surface &surface : layerm->fill_surfaces.surfaces)
-                                    if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
-                                        polygons_append(bridges, surface.expolygon);
-                                diff_polygons = diff(diff_polygons, bridges, true);
-                                polygons_append(bridges, bridged_perimeters);
-                                polygons_append(diff_polygons, 
-                                    intersection(
-                                        // Offset unsupported edges into polygons.
-                                        offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
-                                        bridges));
-                            } else {
-                                // just remove bridged areas
-                                diff_polygons = diff(diff_polygons, layerm->bridged, true);
-                            }
-                        } // if (m_objconfig->dont_support_bridges)
+                        if (this->m_object_config->dont_support_bridges)
+                            SupportMaterialInternal::remove_bridges_from_contacts(
+                                *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons);
 
                         if (diff_polygons.empty())
                             continue;
@@ -842,7 +984,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                             union_ex(diff_polygons, false));
                         #endif /* SLIC3R_DEBUG */
 
-                        if (this->has_contact_loops())
+                        //FIXME the overhang_polygons are used to construct the support towers as well.
+                        //if (this->has_contact_loops())
+                            // Store the exact contour of the overhang for the contact loops.
                             polygons_append(overhang_polygons, diff_polygons);
 
                         // Let's define the required contact area by using a max gap of half the upper 
@@ -851,12 +995,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // on the other side of the object (if it's very thin).
                         {
                             //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
+                            //FIXME one should trim with the layer span colliding with the support layer, this layer
+                            // may be lower than lower_layer, so the support area needed may need to be actually bigger!
+                            // For the same reason, the non-bridging support area may be smaller than the bridging support area!
                             float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy))); 
                             if (slices_margin_cached_offset != slices_margin_offset) {
                                 slices_margin_cached_offset = slices_margin_offset;
                                 slices_margin_cached = (slices_margin_offset == 0.f) ? 
-                                    to_polygons(lower_layer.slices.expolygons) :
-                                    offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                                    lower_layer_polygons :
+                                    offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
                                 if (! buildplate_covered.empty()) {
                                     // Trim the inflated contact surfaces by the top surfaces as well.
                                     polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -879,58 +1026,72 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                     } // for each layer.region
                 } // end of Generate overhang/contact_polygons for non-raft layers.
                 
-                // now apply the contact areas to the layer were they need to be made
+                // Now apply the contact areas to the layer where they need to be made.
                 if (! contact_polygons.empty()) {
-                    // get the average nozzle diameter used on this layer
                     MyLayer     &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
                     new_layer.idx_object_layer_above = layer_id;
-                    if (m_slicing_params.soluble_interface) {
+                    MyLayer     *bridging_layer = nullptr;
+                    if (layer_id == 0) {
+                        // This is a raft contact layer sitting directly on the print bed.
+                        assert(this->has_raft());
+                        new_layer.print_z  = m_slicing_params.raft_contact_top_z;
+                        new_layer.bottom_z = m_slicing_params.raft_interface_top_z; 
+                        new_layer.height   = m_slicing_params.contact_raft_layer_height;
+                    } else if (m_slicing_params.soluble_interface) {
                         // Align the contact surface height with a layer immediately below the supported layer.
-                        new_layer.print_z = layer.print_z - layer.height;
-                        if (layer_id == 0) {
-                            // This is a raft contact layer sitting directly on the print bed.
-                            new_layer.height   = m_slicing_params.contact_raft_layer_height;
-                            new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
-                        } else {
-                            // Interface layer will be synchronized with the object.
-                            assert(layer_id > 0);
-                            new_layer.height = object.layers[layer_id - 1]->height;
-                            new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
-                        }
+                        // Interface layer will be synchronized with the object.
+                        new_layer.print_z  = layer.print_z - layer.height;
+                        new_layer.height   = object.layers[layer_id - 1]->height;
+                        new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
                     } else {
-                        // Contact layer will be printed with a normal flow, but
-                        // it will support layers printed with a bridging flow.
-                        //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow?
-                        // In the future we may switch to a normal extrusion flow for the supported bridges.
-                        // Get the average nozzle diameter used on this layer.
-                        coordf_t nozzle_dmr = 0.;
-                        for (const LayerRegion *region : layer.regions)
-                            nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config);
-                        nozzle_dmr /= coordf_t(layer.regions.size());
-                        new_layer.print_z  = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance;
+                        new_layer.print_z  = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
                         new_layer.bottom_z = new_layer.print_z;
                         new_layer.height   = 0.;
-                        if (layer_id == 0) {
-                            // This is a raft contact layer sitting directly on the print bed.
-                            assert(this->has_raft());
-                            new_layer.bottom_z = m_slicing_params.raft_interface_top_z; 
-                            new_layer.height   = m_slicing_params.contact_raft_layer_height;
+                        // Ignore this contact area if it's too low.
+                        // Don't want to print a layer below the first layer height as it may not stick well.
+                        //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
+                        // and it may actually make sense to do it with a thinner layer than the first layer height.
+                        if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
+                            // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
+                            continue;
+                        } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+                            // Align the layer with the 1st layer height.
+                            new_layer.print_z  = m_slicing_params.first_print_layer_height;
+                            new_layer.bottom_z = 0;
+                            new_layer.height   = m_slicing_params.first_print_layer_height;
                         } else {
-                            // Ignore this contact area if it's too low.
-                            // Don't want to print a layer below the first layer height as it may not stick well.
-                            //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
-                            // and it may actually make sense to do it with a thinner layer than the first layer height.
-                            if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
-                                // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
-                                continue;
-                            } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
-                                // Align the layer with the 1st layer height.
-                                new_layer.print_z  = m_slicing_params.first_print_layer_height;
-                                new_layer.bottom_z = 0;
-                                new_layer.height   = m_slicing_params.first_print_layer_height;
-                            } else {
-                                // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and 
-                                // its height will be set adaptively later on.
+                            // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and 
+                            // its height will be set adaptively later on.
+                        }
+
+                        // Contact layer will be printed with a normal flow, but
+                        // it will support layers printed with a bridging flow.
+                        if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
+                            coordf_t bridging_height = 0.;
+                            for (const LayerRegion *region : layer.regions)
+                                bridging_height += region->region()->bridging_height_avg(*m_print_config);
+                            bridging_height /= coordf_t(layer.regions.size());
+                            coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
+                            if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
+                                // Not below the first layer height means this layer is printable.
+                                if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+                                    // Align the layer with the 1st layer height.
+                                    bridging_print_z = m_slicing_params.first_print_layer_height;
+                                }
+                                if (bridging_print_z < new_layer.print_z - EPSILON) {
+                                    // Allocate the new layer.
+                                    bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
+                                    bridging_layer->idx_object_layer_above = layer_id;
+                                    bridging_layer->print_z = bridging_print_z;
+                                    if (bridging_print_z == m_slicing_params.first_print_layer_height) {
+                                        bridging_layer->bottom_z = 0;
+                                        bridging_layer->height   = m_slicing_params.first_print_layer_height;                                        
+                                    } else {
+                                        // Don't know the height yet.
+                                        bridging_layer->bottom_z = bridging_print_z;
+                                        bridging_layer->height   = 0;
+                                    }
+                                }
                             }
                         }
                     }
@@ -940,27 +1101,112 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         contact_polygons, 
                         // Trimming polygons, to trim the stretched support islands.
                         slices_margin_cached,
-                        // How much to offset the extracted contour outside of the grid.
+                        // Grid resolution.
                         m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
                         Geometry::deg2rad(m_object_config->support_material_angle.value));
-                    // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
-                    new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
-                    // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
-                    new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3));
+                    // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
+                    new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
+                    // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
+                    if (layer_id == 0) {
+                    // if (no_interface_offset == 0.f) {
+                        new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
+                    } else  {
+                        Polygons dense_interface_polygons = diff(overhang_polygons, 
+                            offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+//                            offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                        if (! dense_interface_polygons.empty()) {
+                            //FIXME do it for non-soluble support interfaces only.
+                            //FIXME do it for the bridges only?
+                            SupportGridPattern support_grid_pattern(
+                                // Support islands, to be stretched into a grid.
+                                dense_interface_polygons, 
+                                // Trimming polygons, to trim the stretched support islands.
+                                slices_margin_cached,
+                                // Grid resolution.
+                                m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
+                                Geometry::deg2rad(m_object_config->support_material_angle.value));                        
+                            new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
+                        }
+                    }
 
                     // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
                     // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
                     
                     // Store the overhang polygons.
                     // The overhang polygons are used in the path generator for planning of the contact loops.
-                    // if (this->has_contact_loops())
+                    // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
                     new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
-                    contact_out[layer_id] = &new_layer;
+                    contact_out[layer_id * 2] = &new_layer;
+                    if (bridging_layer != nullptr) {
+                        bridging_layer->polygons          = new_layer.polygons;
+                        bridging_layer->contact_polygons  = new Polygons(*new_layer.contact_polygons);
+                        bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons);
+                        contact_out[layer_id * 2 + 1] = bridging_layer;
+                    }
                 }
             }
         });
+
     // Compress contact_out, remove the nullptr items.
     remove_nulls(contact_out);
+    // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z.
+    std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; });
+
+    // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
+    // the top contact layer is merged into the bottom contact layer.
+    {
+		int i = 0;
+		int k = 0;
+		{
+			// Find the span of layers, which are to be printed at the first layer height.
+			int j = 0;
+			for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
+			if (j > 0) {
+				// Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
+				MyLayer &dst = *contact_out.front();
+				for (int u = 1; u < j; ++ u) {
+					MyLayer &src = *contact_out[u];
+					// The union_() does not support move semantic yet, but maybe one day it will.
+					dst.polygons = union_(dst.polygons, std::move(src.polygons));
+					*dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+					*dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+					// Source polygon is no more needed, it will not be refrenced. Release its data.
+					src.reset();
+				}
+				// Snap the first layer to the 1st layer height.
+				dst.print_z  = m_slicing_params.first_print_layer_height;
+				dst.height   = m_slicing_params.first_print_layer_height;
+				dst.bottom_z = 0;
+				++ k;
+			}
+			i = j;
+		}
+        for (; i < int(contact_out.size()); ++ k) {
+            // Find the span of layers closer than m_support_layer_height_min.
+            int j = i + 1;
+            coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON;
+            for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
+            if (i + 1 < j) {
+                // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
+                MyLayer &dst = *contact_out[i];
+                for (int u = i + 1; u < j; ++ u) {
+                    MyLayer &src = *contact_out[u];
+                    // The union_() does not support move semantic yet, but maybe one day it will.
+                    dst.polygons           = union_(dst.polygons, std::move(src.polygons));
+                    *dst.contact_polygons  = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+                    *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+                    // Source polygon is no more needed, it will not be refrenced. Release its data.
+                    src.reset();
+                }
+            }
+            if (k < i)
+                contact_out[k] = contact_out[i];
+            i = j;
+        }
+        if (k < contact_out.size())
+            contact_out.erase(contact_out.begin() + k, contact_out.end());
+    }
+
     BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
 
     return contact_out;
@@ -996,7 +1242,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
             const Layer &layer = *object.get_layer(layer_id);
             // Collect projections of all contact areas above or at the same level as this top surface.
-            for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
+            for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) {
                 Polygons polygons_new;
                 // Contact surfaces are expanded away from the object, trimmed by the object.
                 // Use a slight positive offset to overlap the touching regions.
@@ -1004,7 +1250,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                 // Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
                 polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON));
 #else
-                // Consume the contact_polygons. The contact polygons are already expanded into a grid form.
+                // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
+                // than the grid cells.
                 polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons));
 #endif
                 // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
@@ -1016,9 +1263,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                 continue;
             Polygons projection_raw = union_(projection);
 
-            // Top surfaces of this layer, to be used to stop the surface volume from growing down.
             tbb::task_group task_group;
             if (! m_object_config->support_material_buildplate_only)
+                // Find the bottom contact layers above the top surfaces of this layer.
                 task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
                     Polygons top = collect_region_slices_by_type(layer, stTop);
         #ifdef SLIC3R_DEBUG
@@ -1046,28 +1293,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                             // Grow top surfaces so that interface and support generation are generated
                             // with some spacing from object - it looks we don't need the actual
                             // top shapes so this can be done here
+                            //FIXME calculate layer height based on the actual thickness of the layer:
+                            // If the layer is extruded with no bridging flow, support just the normal extrusions.
                             layer_new.height  = m_slicing_params.soluble_interface ? 
                                 // Align the interface layer with the object's layer height.
                                 object.layers[layer_id + 1]->height :
                                 // Place a bridge flow interface layer over the top surface.
+                                //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
+                                // According to Jindrich the bottom surfaces work well.
+                                //FIXME test the bridging flow instead?
                                 m_support_material_interface_flow.nozzle_diameter;
                             layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
                                 layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
                             layer_new.bottom_z = layer.print_z;
                             layer_new.idx_object_layer_below = layer_id;
                             layer_new.bridging = ! m_slicing_params.soluble_interface;
-                            //FIXME how much to inflate the top surface?
+                            //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
+                            //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
                             layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
                             if (! m_slicing_params.soluble_interface) {
                                 // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
                                 // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
                                 for (size_t top_idx = size_t(std::max<int>(0, contact_idx)); 
-                                    top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min; 
+                                    top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON; 
                                     ++ top_idx) {
-                                    if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) {
+                                    if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) {
                                         // A top layer has been found, which is close to the new bottom layer.
                                         coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z;
-                                        assert(std::abs(diff) <= this->m_support_layer_height_min);
+                                        assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON);
                                         if (diff > 0.) {
                                             // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
                                             assert(diff < layer_new.height + EPSILON);
@@ -1091,10 +1344,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                                 union_ex(layer_new.polygons, false));
                 #endif /* SLIC3R_DEBUG */
                             // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
+                            //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
                             touching = offset(touching, float(SCALED_EPSILON));
                             for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
                                 const Layer &layer_above = *object.layers[layer_id_above];
-                                if (layer_above.print_z > layer_new.print_z + EPSILON)
+                                if (layer_above.print_z > layer_new.print_z - EPSILON)
                                     break; 
                                 if (! layer_support_areas[layer_id_above].empty()) {
 #ifdef SLIC3R_DEBUG
@@ -1147,7 +1401,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                     projection, 
                     // Trimming polygons, to trim the stretched support islands.
                     trimming,
-                    // How much to offset the extracted contour outside of the grid.
+                    // Grid spacing.
                     m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
                     Geometry::deg2rad(m_object_config->support_material_angle.value));
                 tbb::task_group task_group_inner;
@@ -1158,7 +1412,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                     , &layer
         #endif /* SLIC3R_DEBUG */
                     ] {
-                    layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25);
+                    layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true);
         #ifdef SLIC3R_DEBUG
                     Slic3r::SVG::export_expolygons(
                         debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1172,7 +1426,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                     , &layer
         #endif /* SLIC3R_DEBUG */
                     ] {
-                    projection_new = support_grid_pattern.extract_support(-5);
+                    projection_new = support_grid_pattern.extract_support(-5, true);
         #ifdef SLIC3R_DEBUG
                     Slic3r::SVG::export_expolygons(
                         debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1185,7 +1439,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             task_group.wait();
         }
         std::reverse(bottom_contacts.begin(), bottom_contacts.end());
-        trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+//        trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
+        trim_support_layers_by_object(object, bottom_contacts, 
+            m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, 
+            m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
     } // ! top_contacts.empty()
 
     return bottom_contacts;
@@ -1502,9 +1760,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
                 assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z);
 
                 // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
-                idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, 
-                    [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
-
                 // New polygons for layer_intermediate.
                 Polygons polygons_new;
 
@@ -1523,12 +1778,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
                 // 3) base.print_z > top.print_z  && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
                 // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
                 // 5) base.print_z <= top.print_z  && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
-                int idx_top_contact_overlapping = idx_top_contact_above;
-                while (idx_top_contact_overlapping >= 0 && 
-                       top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON)
-                    -- idx_top_contact_overlapping; 
+                idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, 
+                    [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
                 // Collect all the top_contact layer intersecting with this layer.
-                for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
+                for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
                     MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping];
                     if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON)
                         break;
@@ -1608,7 +1861,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
     ++ iRun;
 #endif /* SLIC3R_DEBUG */
 
-    trim_support_layers_by_object(object, intermediate_layers,  m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min,  m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_gap_xy);
+//    trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
+    this->trim_support_layers_by_object(object, intermediate_layers, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
 }
 
 void PrintObjectSupportMaterial::trim_support_layers_by_object(
@@ -1653,19 +1909,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                     const Layer &object_layer = *object.layers[i];
                     if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
                         break;
-                    polygons_append(polygons_trimming, (Polygons)object_layer.slices);
+                    polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                 }
                 if (! this->m_slicing_params.soluble_interface) {
                     // Collect all bottom surfaces, which will be extruded with a bridging flow.
                     for (; i < object.layers.size(); ++ i) {
                         const Layer &object_layer = *object.layers[i];
                         bool some_region_overlaps = false;
-                        for (LayerRegion* region : object_layer.regions) {
-                            coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*this->m_print_config);
-                            if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON)
+                        for (LayerRegion *region : object_layer.regions) {
+                            coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
+                            if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
                                 break;
                             some_region_overlaps = true;
-                            polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge)));
+                            polygons_append(polygons_trimming, 
+                                offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)), 
+                                       gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                            if (region->region()->config.overhangs.value)
+                                SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
                         }
                         if (! some_region_overlaps)
                             break;
@@ -1675,9 +1935,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                 // perimeter's width. $support contains the full shape of support
                 // 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,
-                    offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
             }
         });
     BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
@@ -1800,11 +2058,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
                     coordf_t top_z    = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z;
                     coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z;
                     // Move idx_top_contact_first up until above the current print_z.
-                    idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; });
+                    idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); //  - EPSILON
                     // Collect the top contact areas above this intermediate layer, below top_z.
                     Polygons polygons_top_contact_projected;
                     for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) {
                         const MyLayer &top_contact_layer = *top_contacts[idx_top_contact];
+                        //FIXME maybe this adds one interface layer in excess?
                         if (top_contact_layer.bottom_z - EPSILON > top_z)
                             break;
                         polygons_append(polygons_top_contact_projected, top_contact_layer.polygons);
@@ -1861,8 +2120,8 @@ static inline void fill_expolygons_generate_paths(
     fill_params.density = density;
     fill_params.complete = true;
     fill_params.dont_adjust = true;
-    for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
-        Surface surface(stInternal, *it_expolygon);
+    for (const ExPolygon &expoly : expolygons) {
+        Surface surface(stInternal, expoly);
         extrusion_entities_append_paths(
             dst,
             filler->fill_surface(&surface, fill_params),
@@ -1883,8 +2142,8 @@ static inline void fill_expolygons_generate_paths(
     fill_params.density = density;
     fill_params.complete = true;
     fill_params.dont_adjust = true;
-    for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
-        Surface surface(stInternal, std::move(*it_expolygon));
+    for (ExPolygon &expoly : expolygons) {
+        Surface surface(stInternal, std::move(expoly));
         extrusion_entities_append_paths(
             dst,
             filler->fill_surface(&surface, fill_params),
@@ -2359,7 +2618,7 @@ void modulate_extrusion_by_overlapping_layers(
                 (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
         }
     private:
-        ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) = delete;
+        ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
         const std::vector<ExtrusionPathFragment> &m_path_fragments;
     };
     const coord_t search_radius = 7;
@@ -2711,6 +2970,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
                     continue;
                 //FIXME When paralellizing, each thread shall have its own copy of the fillers.
                 bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
+                //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
+                // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
                 Flow interface_flow(
                     float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)),
                     float(layer_ex.layer->height),
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index 968763446..dcb3bd5b3 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -12,6 +12,7 @@ class PrintConfig;
 class PrintObjectConfig;
 
 // how much we extend support around the actual contact area
+//FIXME this should be dependent on the nozzle diameter!
 #define SUPPORT_MATERIAL_MARGIN 1.5	
 
 // This class manages raft and supports for a single PrintObject.
@@ -71,6 +72,21 @@ public:
 			overhang_polygons = nullptr;
 		}
 
+		void reset() {
+			layer_type  			= sltUnknown;
+			print_z 				= 0.;
+			bottom_z 				= 0.;
+			height 					= 0.;
+			idx_object_layer_above  = size_t(-1);
+			idx_object_layer_below  = size_t(-1);
+			bridging 				= false;
+			polygons.clear();
+			delete contact_polygons;
+			contact_polygons 		= nullptr;
+			delete overhang_polygons;
+			overhang_polygons 		= nullptr;
+		}
+
 		bool operator==(const MyLayer &layer2) const {
 			return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
 		}
diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp
index 29cfeb1db..9544748e9 100644
--- a/xs/src/libslic3r/SurfaceCollection.hpp
+++ b/xs/src/libslic3r/SurfaceCollection.hpp
@@ -37,6 +37,11 @@ public:
 
     void clear() { surfaces.clear(); }
     bool empty() const { return surfaces.empty(); }
+    bool has(SurfaceType type) const { 
+        for (const Surface &surface : this->surfaces) 
+            if (surface.surface_type == type) return true;
+        return false;
+    }
 
     void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
     void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 6d40731f8..2a1383112 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -21,16 +21,20 @@
 
 #include <Eigen/Dense>
 
+// for SLIC3R_DEBUG_SLICE_PROCESSING
+#include "libslic3r.h"
+
 #if 0
     #define DEBUG
     #define _DEBUG
     #undef NDEBUG
+    #define SLIC3R_DEBUG
+// #define SLIC3R_TRIANGLEMESH_DEBUG
 #endif
 
 #include <assert.h>
 
-#ifdef SLIC3R_DEBUG
-// #define SLIC3R_TRIANGLEMESH_DEBUG
+#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
 #include "SVG.hpp"
 #endif
 
@@ -156,7 +160,6 @@ void TriangleMesh::repair()
     BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
 }
 
-
 float TriangleMesh::volume()
 {
     if (this->stl.stats.volume == -1) 
@@ -320,7 +323,7 @@ bool TriangleMesh::has_multiple_patches() const
         facet_visited[facet_idx] = true;
         for (int j = 0; j < 3; ++ j) {
             int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
-            if (! facet_visited[neighbor_idx])
+            if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
                 facet_queue[facet_queue_cnt ++] = neighbor_idx;
         }
     }
@@ -363,7 +366,7 @@ size_t TriangleMesh::number_of_patches() const
             facet_visited[facet_idx] = true;
             for (int j = 0; j < 3; ++ j) {
                 int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
-                if (! facet_visited[neighbor_idx])
+                if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
                     facet_queue[facet_queue_cnt ++] = neighbor_idx;
             }
         }
@@ -623,10 +626,23 @@ void TriangleMesh::require_shared_vertices()
         BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
         stl_generate_shared_vertices(&(this->stl));
     }
+#ifdef _DEBUG
+    // Verify validity of neighborship data.
+    for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
+        const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
+        const int *vertices = stl.v_indices[facet_idx].vertex;
+        for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
+            int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
+            if (nbr_face != -1) {
+                assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
+                assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
+            }
+        }
+    }
+#endif /* _DEBUG */
     BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
 }
 
-
 TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : 
     mesh(_mesh)
 {
@@ -698,13 +714,13 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
                 }
         }
         // Assign an edge index to the 1st face.
-		this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
+        this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
         if (found) {
             EdgeToFace &edge_j = edges_map[j];
-			this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
-			// Mark the edge as connected.
-			edge_j.face = -1;
-		}
+            this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
+            // Mark the edge as connected.
+            edge_j.face = -1;
+        }
         ++ num_edges;
     }
 }
@@ -771,13 +787,30 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
     {
         static int iRun = 0;
         for (size_t i = 0; i < z.size(); ++ i) {
-            Polygons &polygons = (*layers)[i];
-            SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
+            Polygons  &polygons   = (*layers)[i];
+            ExPolygons expolygons = union_ex(polygons, true);
+            SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
+            {
+                BoundingBox bbox;
+                for (const IntersectionLine &l : lines[i]) {
+                    bbox.merge(l.a);
+                    bbox.merge(l.b);
+                }
+                SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
+                svg.draw(expolygons);
+                for (const IntersectionLine &l : lines[i])
+                    svg.draw(l, "red", 0);
+                svg.draw_outline(expolygons, "black", "blue", 0);
+                svg.Close();
+            }
+#if 0
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
             for (Polygon &poly : polygons) {
                 for (size_t i = 1; i < poly.points.size(); ++ i)
                     assert(poly.points[i-1] != poly.points[i]);
                 assert(poly.points.front() != poly.points.back());
             }
+#endif
         }
         ++ iRun;
     }
@@ -793,91 +826,94 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
     const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
     const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
     
-    #ifdef SLIC3R_DEBUG
+    #ifdef SLIC3R_TRIANGLEMESH_DEBUG
     printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
         facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
         facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
         facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
     printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
-    #endif
+    #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
     
     // find layer extents
     std::vector<float>::const_iterator min_layer, max_layer;
     min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
     max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
-    #ifdef SLIC3R_DEBUG
+    #ifdef SLIC3R_TRIANGLEMESH_DEBUG
     printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
-    #endif
+    #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
     
     for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
         std::vector<float>::size_type layer_idx = it - z.begin();
         IntersectionLine il;
-        if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) {
+        if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
             boost::lock_guard<boost::mutex> l(*lines_mutex);
             if (il.edge_type == feHorizontal) {
-                // Insert all three edges of the face.
+                // Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face
+                // (they may not have a nighbor, or their neighbor is vertical)
                 const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
                 const bool reverse  = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
-                for (int j = 0; j < 3; ++ j) {
-                    int               a_id     = vertices[j % 3];
-                    int               b_id     = vertices[(j+1) % 3];
-                    if (reverse)
-                        std::swap(a_id, b_id);
-                    const stl_vertex &a = this->v_scaled_shared[a_id];
-                    const stl_vertex &b = this->v_scaled_shared[b_id];
-                    il.a(0)    = a(0);
-                    il.a(1)    = a(1);
-                    il.b(0)    = b(0);
-                    il.b(1)    = b(1);
-                    il.a_id   = a_id;
-                    il.b_id   = b_id;
-                    (*lines)[layer_idx].emplace_back(il);
-                }
+                for (int j = 0; j < 3; ++ j)
+                    if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) {
+                        int a_id = vertices[j % 3];
+                        int b_id = vertices[(j+1) % 3];
+                        if (reverse)
+                            std::swap(a_id, b_id);
+                        const stl_vertex &a = this->v_scaled_shared[a_id];
+                        const stl_vertex &b = this->v_scaled_shared[b_id];
+                        il.a(0)    = a(0);
+                        il.a(1)    = a(1);
+                        il.b(0)    = b(0);
+                        il.b(1)    = b(1);
+                        il.a_id   = a_id;
+                        il.b_id   = b_id;
+                        assert(il.a != il.b);
+                        // This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces.
+                        il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0);
+                        (*lines)[layer_idx].emplace_back(il);
+                    }
             } else
                 (*lines)[layer_idx].emplace_back(il);
         }
     }
 }
 
-void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
+void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
 {
     std::vector<Polygons> layers_p;
     this->slice(z, &layers_p);
     
-	BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
-	layers->resize(z.size());
-	tbb::parallel_for(
-		tbb::blocked_range<size_t>(0, z.size()),
-		[&layers_p, layers, this](const tbb::blocked_range<size_t>& range) {
-    		for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
+    BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
+    layers->resize(z.size());
+    tbb::parallel_for(
+        tbb::blocked_range<size_t>(0, z.size()),
+        [&layers_p, layers, this](const tbb::blocked_range<size_t>& range) {
+            for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
 #ifdef SLIC3R_TRIANGLEMESH_DEBUG
-    			printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
+                printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
 #endif
                 this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
-    		}
-    	});
-	BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
+            }
+        });
+    BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
 }
 
 // Return true, if the facet has been sliced and line_out has been filled.
-bool TriangleMeshSlicer::slice_facet(
+TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
     float slice_z, const stl_facet &facet, const int facet_idx,
     const float min_z, const float max_z, 
     IntersectionLine *line_out) const
 {
     IntersectionPoint points[3];
     size_t            num_points = 0;
-    size_t            points_on_layer[3];
-    size_t            num_points_on_layer = 0;
+    size_t            point_on_layer = size_t(-1);
     
     // Reorder vertices so that the first one is the one with lowest Z.
     // This is needed to get all intersection lines in a consistent order
     // (external on the right of the line)
+    const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
     int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
-    for (int j = i; j - i < 3; ++ j) {  // loop through facet edges
+    for (int j = i; j - i < 3; ++j ) {  // loop through facet edges
         int               edge_id  = this->facets_edges[facet_idx * 3 + (j % 3)];
-        const int        *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
         int               a_id     = vertices[j % 3];
         int               b_id     = vertices[(j+1) % 3];
         const stl_vertex &a = this->v_scaled_shared[a_id];
@@ -890,116 +926,279 @@ bool TriangleMeshSlicer::slice_facet(
             const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
             const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
             bool              swap = false;
+            const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
+            // We may ignore this edge for slicing purposes, but we may still use it for object cutting.
+            FacetSliceType    result = Slicing;
+            const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
             if (min_z == max_z) {
                 // All three vertices are aligned with slice_z.
                 line_out->edge_type = feHorizontal;
-                if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) {
+                // Mark neighbor edges, which do not have a neighbor.
+                uint32_t edges = 0;
+                for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) {
+                    // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
+                    // opposite face, add it to the edges to process when slicing.
+                    if (nbr.neighbor[nbr_idx] == -1) {
+                        // Mark this edge to be added to the slice.
+                        edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx);
+                    }
+#if 1
+                     else if (normal(2) > 0) {
+                        // Produce edges for opposite faced overlapping horizontal faces aka folds.
+                        // This method often produces connecting lines (noise) at the cutting plane.
+                        // Produce the edges for the top facing face of the pair of top / bottom facing faces.
+
+                        // Index of a neighbor face.
+                        const int  nbr_face     = nbr.neighbor[nbr_idx];
+                        const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex;
+                        int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]];
+                        const stl_vertex    &c2 = this->v_scaled_shared[idx_vertex_opposite];
+                        if (c2(2) == slice_z) {
+                            // Edge shared by facet_idx and nbr_face.
+                            int               a_id      = vertices[nbr_idx];
+                            int               b_id      = vertices[(nbr_idx + 1) % 3];
+                            int               c1_id     = vertices[(nbr_idx + 2) % 3];
+                            const stl_vertex &a         = this->v_scaled_shared[a_id];
+                            const stl_vertex &b         = this->v_scaled_shared[b_id];
+                            const stl_vertex &c1        = this->v_scaled_shared[c1_id];
+                            // Verify that the two neighbor faces share a common edge.
+                            assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
+                            assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
+                            double n1 = (double(c1(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c1(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
+                            double n2 = (double(c2(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c2(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
+                            if (n1 * n2 > 0)
+                                // The two faces overlap. This indicates an invalid mesh geometry (non-manifold),
+                                // but these are the real world objects, and leaving out these edges leads to missing contours.
+                                edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx);
+                         }
+                    }
+#endif
+                }
+                // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
+                result = (edges == 0) ? Cutting : Slicing;
+                line_out->flags |= edges;
+                if (normal(2) < 0) {
                     // If normal points downwards this is a bottom horizontal facet so we reverse its point order.
                     swap = true;
                 }
-            } else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) {
-                // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
-                line_out->edge_type = feTop;
-                swap = true;
             } else {
-                // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
-                line_out->edge_type = feBottom;
+                // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
+                int  nbr_idx     = j % 3;
+                int  nbr_face    = nbr.neighbor[nbr_idx];
+                // Is the third vertex below the cutting plane?
+                bool third_below = v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z;
+                // Is this a concave corner?
+                if (nbr_face == -1) {
+#ifdef _DEBUG
+                    printf("Face has no neighbor!\n");
+#endif
+                } else {
+                    assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
+                    assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
+                    int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
+                    const stl_vertex &c = this->v_scaled_shared[idx_vertex_opposite];
+                    if (c(2) == slice_z) {
+                        double normal_nbr = (double(c(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
+#if 0
+                        if ((normal_nbr < 0) == third_below) {
+                            printf("Flipped normal?\n");
+                        }
+#endif
+                        result =
+                                // A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
+                                // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
+                                // and leth the code downstream hopefully handle it.
+    #if 1
+                                // Ignore concave corners for slicing.
+                                // This method has the unfortunate property, that folds in a horizontal plane create concave corners,
+                                // leading to broken contours, if these concave corners are not replaced by edges of the folds, see above.
+                                   ((normal_nbr < 0) == third_below) ? Cutting : Slicing;
+    #else
+                                // Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing,
+                                // and rightly so.
+                                    Slicing;
+    #endif
+                    } else {
+                        // For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index.
+                        result = (facet_idx < nbr_face) ? Slicing : Cutting;
+                    }
+                }
+                if (third_below) {
+                    line_out->edge_type = feTop;
+                    swap = true;
+                } else
+                    line_out->edge_type = feBottom;
             }
             line_out->a = to_2d(swap ? b : a).cast<coord_t>();
             line_out->b = to_2d(swap ? a : b).cast<coord_t>();
             line_out->a_id = swap ? b_id : a_id;
             line_out->b_id = swap ? a_id : b_id;
-            return true;
+            assert(line_out->a != line_out->b);
+            return result;
         }
 
         if (a(2) == slice_z) {
             // Only point a alings with the cutting plane.
-            points_on_layer[num_points_on_layer ++] = num_points;
-            IntersectionPoint &point = points[num_points ++];
-            point(0)       = a(0);
-            point(1)       = a(1);
-            point.point_id  = a_id;
+            if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+                point_on_layer = num_points;
+                IntersectionPoint &point = points[num_points ++];
+                point(0)       = a(0);
+                point(1)       = a(1);
+                point.point_id  = a_id;
+            }
         } else if (b(2) == slice_z) {
             // Only point b alings with the cutting plane.
-            points_on_layer[num_points_on_layer ++] = num_points;
-            IntersectionPoint &point = points[num_points ++];
-            point(0)       = b(0);
-            point(1)       = b(1);
-            point.point_id  = b_id;
+            if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+                point_on_layer = num_points;
+                IntersectionPoint &point = points[num_points ++];
+                point(0)       = b(0);
+                point(1)       = b(1);
+                point.point_id  = b_id;
+            }
         } else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
             // A general case. The face edge intersects the cutting plane. Calculate the intersection point.
-            IntersectionPoint &point = points[num_points ++];
-            point(0)       = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2));
-            point(1)       = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2));
-            point.edge_id   = edge_id;
+            assert(a_id != b_id);
+            // Sort the edge to give a consistent answer.
+            const stl_vertex *pa = &a;
+            const stl_vertex *pb = &b;
+            if (a_id > b_id) {
+                std::swap(a_id, b_id);
+                std::swap(pa, pb);
+            }
+            IntersectionPoint &point = points[num_points];
+            double t = (double(slice_z) - double((*pb)(2))) / (double((*pa)(2)) - double((*pb)(2)));
+            if (t <= 0.) {
+                if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+                    point(0) = (*pa)(0);
+                    point(1) = (*pa)(1);
+                    point_on_layer = num_points ++;
+                    point.point_id = a_id;
+                }
+            } else if (t >= 1.) {
+                if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+                    point(0) = (*pb)(0);
+                    point(1) = (*pb)(1);
+                    point_on_layer = num_points ++;
+                    point.point_id = b_id;
+                }
+            } else {
+                point(0) = coord_t(floor(double((*pb)(0)) + (double((*pa)(0)) - double((*pb)(0))) * t + 0.5));
+                point(1) = coord_t(floor(double((*pb)(1)) + (double((*pa)(1)) - double((*pb)(1))) * t + 0.5));
+                point.edge_id = edge_id;
+                ++ num_points;
+            }
         }
     }
 
-    // We can't have only one point on layer because each vertex gets detected
-    // twice (once for each edge), and we can't have three points on layer,
-    // because we assume this code is not getting called for horizontal facets.
-    assert(num_points_on_layer == 0 || num_points_on_layer == 2);
-    if (num_points_on_layer > 0) {
-        assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
-        assert(num_points == 2 || num_points == 3);
-        if (num_points < 3)
-            // This triangle touches the cutting plane with a single vertex. Ignore it.
-            return false;
-        // Erase one of the duplicate points.
-        -- num_points;
-        for (int i = points_on_layer[1]; i < num_points; ++ i)
-            points[i] = points[i + 1];
-    }
-    
-    // Facets must intersect each plane 0 or 2 times.
-    assert(num_points == 0 || num_points == 2);
+    // Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
+    assert(num_points < 3);
     if (num_points == 2) {
-        line_out->edge_type  = feNone;
+        line_out->edge_type  = feGeneral;
         line_out->a          = (Point)points[1];
         line_out->b          = (Point)points[0];
         line_out->a_id       = points[1].point_id;
         line_out->b_id       = points[0].point_id;
         line_out->edge_a_id  = points[1].edge_id;
         line_out->edge_b_id  = points[0].edge_id;
-        return true;
+        // Not a zero lenght edge.
+        //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+        //assert(line_out->a != line_out->b);
+        // The plane cuts at least one edge in a general position.
+        assert(line_out->a_id == -1 || line_out->b_id == -1);
+        assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
+        // General slicing position, use the segment for both slicing and object cutting.
+#if 0
+        if (line_out->a_id != -1 && line_out->b_id != -1) {
+            // Solving a degenerate case, where both the intersections snapped to an edge.
+            // Correctly classify the face as below or above based on the position of the 3rd point.
+            int i = vertices[0];
+            if (i == line_out->a_id || i == line_out->b_id)
+                i = vertices[1];
+            if (i == line_out->a_id || i == line_out->b_id)
+                i = vertices[2];
+            assert(i != line_out->a_id && i != line_out->b_id);
+            line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
+        }
+#endif
+        return Slicing;
+    }
+    return NoSlice;
+}
+
+//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
+// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
+// So the following code makes only sense now to handle degenerate meshes with more than two faces
+// sharing a single edge.
+static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
+{
+    std::vector<IntersectionLine*> by_vertex_pair;
+    by_vertex_pair.reserve(lines.size());
+    for (IntersectionLine& line : lines)
+        if (line.edge_type != feGeneral && line.a_id != -1)
+            // This is a face edge. Check whether there is its neighbor stored in lines.
+            by_vertex_pair.emplace_back(&line);
+    auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
+        // Sort vertices of l1, l2 lexicographically
+        int l1a = l1->a_id;
+        int l1b = l1->b_id;
+        int l2a = l2->a_id;
+        int l2b = l2->b_id;
+        if (l1a > l1b)
+            std::swap(l1a, l1b);
+        if (l2a > l2b)
+            std::swap(l2a, l2b);
+        // Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
+        return l1a < l2a || (l1a == l2a && l1b < l2b);
+    };
+    std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
+    for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
+        IntersectionLine &l1 = **line;
+        if (! l1.skip()) {
+            // Iterate as long as line and line2 edges share the same end points.
+            for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
+                // Lines must share the end points.
+                assert(! edges_lower_sorted(*line, *line2));
+                assert(! edges_lower_sorted(*line2, *line));
+                IntersectionLine &l2 = **line2;
+                if (l2.skip())
+                    continue;
+                if (l1.a_id == l2.a_id) {
+                    assert(l1.b_id == l2.b_id);
+                    l2.set_skip();
+                    // If they are both oriented upwards or downwards (like a 'V'),
+                    // then we can remove both edges from this layer since it won't 
+                    // affect the sliced shape.
+                    // If one of them is oriented upwards and the other is oriented
+                    // downwards, let's only keep one of them (it doesn't matter which
+                    // one since all 'top' lines were reversed at slicing).
+                    if (l1.edge_type == l2.edge_type) {
+                        l1.set_skip();
+                        break;
+                    }
+                } else {
+                    assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
+                    // If this edge joins two horizontal facets, remove both of them.
+                    if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
+                        l1.set_skip();
+                        l2.set_skip();
+                        break;
+                    }
+                }
+            }
+        }
     }
-    return false;
 }
 
 void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
 {
-    // Remove tangent edges.
-    //FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane.
-    for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line)
-        if (! line->skip && line->edge_type != feNone) {
-            // This line is af facet edge. There may be a duplicate line with the same end vertices.
-            // If the line is is an edge connecting two facets, find another facet edge
-            // having the same endpoints but in reverse order.
-            for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2)
-                if (! line2->skip && line2->edge_type != feNone) {
-                    // Are these facets adjacent? (sharing a common edge on this layer)
-                    if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
-                        line2->skip = true;
-                        /* if they are both oriented upwards or downwards (like a 'V')
-                           then we can remove both edges from this layer since it won't 
-                           affect the sliced shape */
-                        /* if one of them is oriented upwards and the other is oriented
-                           downwards, let's only keep one of them (it doesn't matter which
-                           one since all 'top' lines were reversed at slicing) */
-                        if (line->edge_type == line2->edge_type) {
-                            line->skip = true;
-                            break;
-                        }
-                    } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
-                        /* if this edge joins two horizontal facets, remove both of them */
-                        if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
-                            line->skip = true;
-                            line2->skip = true;
-                            break;
-                        }
-                    }
-                }
-        }
+#if 0
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+//#ifdef _DEBUG
+    for (const Line &l : lines)
+        assert(l.a != l.b);
+#endif /* _DEBUG */
+
+    remove_tangent_edges(lines);
 
     struct OpenPolyline {
         OpenPolyline() {};
@@ -1022,7 +1221,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
         by_edge_a_id.reserve(lines.size());
         by_a_id.reserve(lines.size());
         for (IntersectionLine &line : lines) {
-            if (! line.skip) {
+            if (! line.skip()) {
                 if (line.edge_a_id != -1)
                     by_edge_a_id.emplace_back(&line);
                 if (line.a_id != -1)
@@ -1039,13 +1238,14 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
             // take first spare line and start a new loop
             IntersectionLine *first_line = nullptr;
             for (; it_line_seed != lines.end(); ++ it_line_seed)
-                if (! it_line_seed->skip) {
+                if (it_line_seed->is_seed_candidate()) {
+                //if (! it_line_seed->skip()) {
                     first_line = &(*it_line_seed ++);
                     break;
                 }
             if (first_line == nullptr)
                 break;
-            first_line->skip = true;
+            first_line->set_skip();
             Points loop_pts;
             loop_pts.emplace_back(first_line->a);
             IntersectionLine *last_line = first_line;
@@ -1066,7 +1266,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                     if (it_begin != by_edge_a_id.end()) {
                         auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
                         for (auto it_line = it_begin; it_line != it_end; ++ it_line)
-                            if (! (*it_line)->skip) {
+                            if (! (*it_line)->skip()) {
                                 next_line = *it_line;
                                 break;
                             }
@@ -1078,7 +1278,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                     if (it_begin != by_a_id.end()) {
                         auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
                         for (auto it_line = it_begin; it_line != it_end; ++ it_line)
-                            if (! (*it_line)->skip) {
+                            if (! (*it_line)->skip()) {
                                 next_line = *it_line;
                                 break;
                             }
@@ -1109,7 +1309,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                 */
                 loop_pts.emplace_back(next_line->a);
                 last_line = next_line;
-                next_line->skip = true;
+                next_line->set_skip();
             }
         }
     }
@@ -1176,12 +1376,12 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                             }
                     }
                 }
-				if (next_start == nullptr) {
-					// The current loop could not be closed. Unmark the segment.
-					opl.consumed = false;
-					break;
-				}
-				// Attach this polyline to the end of the initial polyline.
+                if (next_start == nullptr) {
+                    // The current loop could not be closed. Unmark the segment.
+                    opl.consumed = false;
+                    break;
+                }
+                // Attach this polyline to the end of the initial polyline.
                 if (next_start->start) {
                     auto it = next_start->polyline->points.begin();
                     std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
@@ -1201,8 +1401,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                 if ((ip1.edge_id  != -1 && ip1.edge_id  == ip2.edge_id) ||
                     (ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
                     // The current loop is complete. Add it to the output.
-                    /*assert(opl.points.front().point_id == opl.points.back().point_id);
-                    assert(opl.points.front().edge_id  == opl.points.back().edge_id);*/
+                    //assert(opl.points.front().point_id == opl.points.back().point_id);
+                    //assert(opl.points.front().edge_id  == opl.points.back().edge_id);
                     // Remove the duplicate last point.
                     opl.points.pop_back();
                     if (opl.points.size() >= 3) {
@@ -1217,9 +1417,9 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                         loops->emplace_back(std::move(opl.points));
                     }
                     opl.points.clear();
-					break;
+                    break;
                 }
-				// Continue with the current loop.
+                // Continue with the current loop.
             }
         }
     }
@@ -1267,15 +1467,15 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
         if (slice_idx == -1)
             // Ignore this hole.
             continue;
-		assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
-		(*slices)[slice_idx].holes.emplace_back(std::move(*hole));
+        assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
+        (*slices)[slice_idx].holes.emplace_back(std::move(*hole));
     }
 
 #if 0
     // If the input mesh is not valid, the holes may intersect with the external contour.
     // Rather subtract them from the outer contour.
     Polygons poly;
-	for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
+    for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
         if (it_slice->holes.empty()) {
             poly.emplace_back(std::move(it_slice->contour));
         } else {
@@ -1285,7 +1485,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
                 it->reverse();
             polygons_append(poly, diff(contours, it_slice->holes));
         }
-	}
+    }
     // If the input mesh is not valid, the input contours may intersect.
     *slices = union_ex(poly);
 #endif
@@ -1402,7 +1602,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
         
         // intersect facet with cutting plane
         IntersectionLine line;
-        if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
+        if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
             // Save intersection lines for generating correct triangulations.
             if (line.edge_type == feTop) {
                 lower_lines.emplace_back(line);
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index aebed4a2e..b1f316fde 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -82,7 +82,7 @@ private:
 
 enum FacetEdgeType { 
     // A general case, the cutting plane intersect a face at two different edges.
-    feNone,
+    feGeneral,
     // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
     feTop,
     // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
@@ -116,6 +116,14 @@ public:
 class IntersectionLine : public Line
 {
 public:
+    IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
+
+    bool skip() const { return (this->flags & SKIP) != 0; }
+    void set_skip() { this->flags |= SKIP; }
+
+    bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
+    void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
+    
     // Inherits Point a, b
     // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
     // Vertex indices of the line end points.
@@ -124,11 +132,23 @@ public:
     // Source mesh edges of the line end points.
     int             edge_a_id;
     int             edge_b_id;
-    // feNone, feTop, feBottom, feHorizontal
+    // feGeneral, feTop, feBottom, feHorizontal
     FacetEdgeType   edge_type;
-    // Used by TriangleMeshSlicer::make_loops() to skip duplicate edges.
-    bool            skip;
-    IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {};
+    // Used by TriangleMeshSlicer::slice() to skip duplicate edges.
+    enum {
+        // Triangle edge added, because it has no neighbor.
+        EDGE0_NO_NEIGHBOR   = 0x001,
+        EDGE1_NO_NEIGHBOR   = 0x002,
+        EDGE2_NO_NEIGHBOR   = 0x004,
+        // Triangle edge added, because it makes a fold with another horizontal edge.
+        EDGE0_FOLD          = 0x010,
+        EDGE1_FOLD          = 0x020,
+        EDGE2_FOLD          = 0x040,
+        // The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
+        NO_SEED             = 0x100,
+        SKIP                = 0x200,
+    };
+    uint32_t        flags;
 };
 typedef std::vector<IntersectionLine> IntersectionLines;
 typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
@@ -139,7 +159,12 @@ public:
     TriangleMeshSlicer(TriangleMesh* _mesh);
     void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
     void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
-    bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
+    enum FacetSliceType {
+        NoSlice = 0,
+        Slicing = 1,
+        Cutting = 2
+    };
+    FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
         const float min_z, const float max_z, IntersectionLine *line_out) const;
     void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
     
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index f1390b8a2..ab408f397 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -9,6 +9,7 @@ namespace Slic3r {
 
 extern void set_logging_level(unsigned int level);
 extern void trace(unsigned int level, const char *message);
+extern void disable_multi_threading();
 
 // Set a path with GUI resource files.
 void set_var_dir(const std::string &path);
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 95aaf5453..d6f8ff595 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -24,6 +24,8 @@
 #include <boost/nowide/integration/filesystem.hpp>
 #include <boost/nowide/convert.hpp>
 
+#include <tbb/task_scheduler_init.h>
+
 namespace Slic3r {
 
 static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
@@ -82,6 +84,14 @@ void trace(unsigned int level, const char *message)
         (::boost::log::keywords::severity = severity)) << message;
 }
 
+void disable_multi_threading()
+{
+    // Disable parallelization so the Shiny profiler works
+    static tbb::task_scheduler_init *tbb_init = nullptr;
+    if (tbb_init == nullptr)
+        tbb_init = new tbb::task_scheduler_init(1);
+}
+
 static std::string g_var_dir;
 
 void set_var_dir(const std::string &dir)
diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp
index 2c34a4749..6438a6d43 100644
--- a/xs/src/slic3r/AppController.cpp
+++ b/xs/src/slic3r/AppController.cpp
@@ -11,13 +11,11 @@
 #include <ModelArrange.hpp>
 #include <slic3r/GUI/PresetBundle.hpp>
 
+#include <Geometry.hpp>
 #include <PrintConfig.hpp>
 #include <Print.hpp>
-#include <PrintExport.hpp>
-#include <Geometry.hpp>
 #include <Model.hpp>
 #include <Utils.hpp>
-#include <SLABasePool.hpp>
 
 namespace Slic3r {
 
@@ -45,15 +43,6 @@ namespace GUI {
 PresetBundle* get_preset_bundle();
 }
 
-static const PrintObjectStep STEP_SLICE                 = posSlice;
-static const PrintObjectStep STEP_PERIMETERS            = posPerimeters;
-static const PrintObjectStep STEP_PREPARE_INFILL        = posPrepareInfill;
-static const PrintObjectStep STEP_INFILL                = posInfill;
-static const PrintObjectStep STEP_SUPPORTMATERIAL       = posSupportMaterial;
-static const PrintStep STEP_SKIRT                       = psSkirt;
-static const PrintStep STEP_BRIM                        = psBrim;
-static const PrintStep STEP_WIPE_TOWER                  = psWipeTower;
-
 AppControllerBoilerplate::ProgresIndicatorPtr
 AppControllerBoilerplate::global_progress_indicator() {
     ProgresIndicatorPtr ret;
@@ -73,336 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator(
     pri_data_->m.unlock();
 }
 
-void PrintController::make_skirt()
-{
-    assert(print_ != nullptr);
-
-    // prerequisites
-    for(auto obj : print_->objects) make_perimeters(obj);
-    for(auto obj : print_->objects) infill(obj);
-    for(auto obj : print_->objects) gen_support_material(obj);
-
-    if(!print_->state.is_done(STEP_SKIRT)) {
-        print_->state.set_started(STEP_SKIRT);
-        print_->skirt.clear();
-        if(print_->has_skirt()) print_->_make_skirt();
-
-        print_->state.set_done(STEP_SKIRT);
-    }
-}
-
-void PrintController::make_brim()
-{
-    assert(print_ != nullptr);
-
-    // prerequisites
-    for(auto obj : print_->objects) make_perimeters(obj);
-    for(auto obj : print_->objects) infill(obj);
-    for(auto obj : print_->objects) gen_support_material(obj);
-    make_skirt();
-
-    if(!print_->state.is_done(STEP_BRIM)) {
-        print_->state.set_started(STEP_BRIM);
-
-        // since this method must be idempotent, we clear brim paths *before*
-        // checking whether we need to generate them
-        print_->brim.clear();
-
-        if(print_->config.brim_width > 0) print_->_make_brim();
-
-        print_->state.set_done(STEP_BRIM);
-    }
-}
-
-void PrintController::make_wipe_tower()
-{
-    assert(print_ != nullptr);
-
-    // prerequisites
-    for(auto obj : print_->objects) make_perimeters(obj);
-    for(auto obj : print_->objects) infill(obj);
-    for(auto obj : print_->objects) gen_support_material(obj);
-    make_skirt();
-    make_brim();
-
-    if(!print_->state.is_done(STEP_WIPE_TOWER)) {
-        print_->state.set_started(STEP_WIPE_TOWER);
-
-        // since this method must be idempotent, we clear brim paths *before*
-        // checking whether we need to generate them
-        print_->brim.clear();
-
-        if(print_->has_wipe_tower()) print_->_make_wipe_tower();
-
-        print_->state.set_done(STEP_WIPE_TOWER);
-    }
-}
-
-void PrintController::slice(PrintObject *pobj)
-{
-    assert(pobj != nullptr && print_ != nullptr);
-
-    if(pobj->state.is_done(STEP_SLICE)) return;
-
-    pobj->state.set_started(STEP_SLICE);
-
-    pobj->_slice();
-
-    auto msg = pobj->_fix_slicing_errors();
-    if(!msg.empty()) report_issue(IssueType::WARN, msg);
-
-    // simplify slices if required
-    if (print_->config.resolution)
-        pobj->_simplify_slices(scale_(print_->config.resolution));
-
-
-    if(pobj->layers.empty())
-        report_issue(IssueType::ERR,
-                     _(L("No layers were detected. You might want to repair your "
-                     "STL file(s) or check their size or thickness and retry"))
-                     );
-
-    pobj->state.set_done(STEP_SLICE);
-}
-
-void PrintController::make_perimeters(PrintObject *pobj)
-{
-    assert(pobj != nullptr);
-
-    slice(pobj);
-
-    if (!pobj->state.is_done(STEP_PERIMETERS)) {
-        pobj->_make_perimeters();
-    }
-}
-
-void PrintController::infill(PrintObject *pobj)
-{
-    assert(pobj != nullptr);
-
-    make_perimeters(pobj);
-
-    if (!pobj->state.is_done(STEP_PREPARE_INFILL)) {
-        pobj->state.set_started(STEP_PREPARE_INFILL);
-
-        pobj->_prepare_infill();
-
-        pobj->state.set_done(STEP_PREPARE_INFILL);
-    }
-
-    pobj->_infill();
-}
-
-void PrintController::gen_support_material(PrintObject *pobj)
-{
-    assert(pobj != nullptr);
-
-    // prerequisites
-    slice(pobj);
-
-    if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) {
-        pobj->state.set_started(STEP_SUPPORTMATERIAL);
-
-        pobj->clear_support_layers();
-
-        if((pobj->config.support_material || pobj->config.raft_layers > 0)
-                && pobj->layers.size() > 1) {
-            pobj->_generate_support_material();
-        }
-
-        pobj->state.set_done(STEP_SUPPORTMATERIAL);
-    }
-}
-
-PrintController::PngExportData
-PrintController::query_png_export_data(const DynamicPrintConfig& conf)
-{
-    PngExportData ret;
-
-    auto zippath = query_destination_path("Output zip file", "*.zip", "out");
-
-    ret.zippath = zippath;
-
-    ret.width_mm = conf.opt_float("display_width");
-    ret.height_mm = conf.opt_float("display_height");
-
-    ret.width_px = conf.opt_int("display_pixels_x");
-    ret.height_px = conf.opt_int("display_pixels_y");
-
-    auto opt_corr = conf.opt<ConfigOptionFloats>("printer_correction");
-
-    if(opt_corr) {
-        ret.corr_x = opt_corr->values[0];
-        ret.corr_y = opt_corr->values[1];
-        ret.corr_z = opt_corr->values[2];
-    }
-
-    ret.exp_time_first_s = conf.opt_float("initial_exposure_time");
-    ret.exp_time_s = conf.opt_float("exposure_time");
-
-    return ret;
-}
-
-void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri)
-{
-    auto st = pri->state();
-
-    Slic3r::trace(3, "Starting the slicing process.");
-
-    pri->update(st+20, _(L("Generating perimeters")));
-    for(auto obj : print_->objects) make_perimeters(obj);
-
-    pri->update(st+60, _(L("Infilling layers")));
-    for(auto obj : print_->objects) infill(obj);
-
-    pri->update(st+70, _(L("Generating support material")));
-    for(auto obj : print_->objects) gen_support_material(obj);
-
-    pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")),
-                     print_->total_weight, print_->total_cost);
-    pri->state(st+85);
-
-
-    pri->update(st+88, _(L("Generating skirt")));
-    make_skirt();
-
-
-    pri->update(st+90, _(L("Generating brim")));
-    make_brim();
-
-    pri->update(st+95, _(L("Generating wipe tower")));
-    make_wipe_tower();
-
-    pri->update(st+100, _(L("Done")));
-
-    // time to make some statistics..
-
-    Slic3r::trace(3, _(L("Slicing process finished.")));
-}
-
-void PrintController::slice()
-{
-    auto pri = global_progress_indicator();
-    if(!pri) pri = create_progress_indicator(100, L("Slicing"));
-    slice(pri);
-}
-
-void PrintController::slice_to_png()
-{
-    using Pointf3 = Vec3d;
-
-    auto presetbundle = GUI::get_preset_bundle();
-
-    assert(presetbundle);
-
-    auto pt = presetbundle->printers.get_selected_preset().printer_technology();
-    if(pt != ptSLA) {
-        report_issue(IssueType::ERR, _("Printer technology is not SLA!"),
-                     _("Error"));
-        return;
-    }
-
-    auto conf = presetbundle->full_config();
-    conf.validate();
-
-    auto exd = query_png_export_data(conf);
-    if(exd.zippath.empty()) return;
-
-    try {
-        print_->apply_config(conf);
-        print_->validate();
-    } catch(std::exception& e) {
-        report_issue(IssueType::ERR, e.what(), "Error");
-        return;
-    }
-
-    // TODO: copy the model and work with the copy only
-    bool correction = false;
-    if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
-        correction = true;
-        print_->invalidate_all_steps();
-
-        for(auto po : print_->objects) {
-            po->model_object()->scale(
-                        Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
-                        );
-            po->model_object()->invalidate_bounding_box();
-            po->reload_model_instances();
-            po->invalidate_all_steps();
-        }
-    }
-
-    // Turn back the correction scaling on the model.
-    auto scale_back = [this, correction, exd]() {
-        if(correction) { // scale the model back
-            print_->invalidate_all_steps();
-            for(auto po : print_->objects) {
-                po->model_object()->scale(
-                    Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
-                );
-                po->model_object()->invalidate_bounding_box();
-                po->reload_model_instances();
-                po->invalidate_all_steps();
-            }
-        }
-    };
-
-    auto print_bb = print_->bounding_box();
-    Vec2d punsc = unscale(print_bb.size());
-
-    // If the print does not fit into the print area we should cry about it.
-    if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) {
-        std::stringstream ss;
-
-        ss << _(L("Print will not fit and will be truncated!")) << "\n"
-           << _(L("Width needed: ")) << px(punsc) << " mm\n"
-           << _(L("Height needed: ")) << py(punsc) << " mm\n";
-
-       if(!report_issue(IssueType::WARN_Q, ss.str(), _(L("Warning"))))  {
-           scale_back();
-           return;
-       }
-    }
-
-//    std::async(supports_asynch()? std::launch::async : std::launch::deferred,
-//                   [this, exd, scale_back]()
-//    {
-
-        auto pri = create_progress_indicator(
-                    200, _(L("Slicing to zipped png files...")));
-
-        try {
-            pri->update(0, _(L("Slicing...")));
-            slice(pri);
-        } catch (std::exception& e) {
-            pri->cancel();
-            report_issue(IssueType::ERR, e.what(), _(L("Exception occured")));
-            scale_back();
-            return;
-        }
-
-        auto pbak = print_->progressindicator;
-        print_->progressindicator = pri;
-
-        try {
-            print_to<FilePrinterFormat::PNG>( *print_, exd.zippath,
-                        exd.width_mm, exd.height_mm,
-                        exd.width_px, exd.height_px,
-                        exd.exp_time_s, exd.exp_time_first_s);
-
-        } catch (std::exception& e) {
-            pri->cancel();
-            report_issue(IssueType::ERR, e.what(), _(L("Exception occured")));
-        }
-
-        print_->progressindicator = pbak;
-        scale_back();
-
-//    });
-}
-
 void ProgressIndicator::message_fmt(
-        const string &fmtstr, ...) {
+        const std::string &fmtstr, ...) {
     std::stringstream ss;
     va_list args;
     va_start(args, fmtstr);
@@ -433,80 +94,77 @@ const PrintConfig &PrintController::config() const
     return print_->config;
 }
 
-
 void AppController::arrange_model()
 {
-    using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
+    auto ftr = std::async(
+               supports_asynch()? std::launch::async : std::launch::deferred,
+               [this]()
+    {
+        using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
 
-    if(arranging_.load()) return;
+        unsigned count = 0;
+        for(auto obj : model_->objects) count += obj->instances.size();
 
-    // to prevent UI reentrancies
-    arranging_.store(true);
+        auto pind = global_progress_indicator();
 
-    unsigned count = 0;
-    for(auto obj : model_->objects) count += obj->instances.size();
+        float pmax = 1.0;
 
-    auto pind = global_progress_indicator();
+        if(pind) {
+            pmax = pind->max();
 
-    float pmax = 1.0;
+            // Set the range of the progress to the object count
+            pind->max(count);
 
-    if(pind) {
-        pmax = pind->max();
+        }
 
-        // Set the range of the progress to the object count
-        pind->max(count);
+        auto dist = print_ctl()->config().min_object_distance();
 
-        pind->on_cancel([this](){
-            arranging_.store(false);
-        });
+        // Create the arranger config
+        auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
+
+        auto& bedpoints = print_ctl()->config().bed_shape.values;
+        Polyline bed; bed.points.reserve(bedpoints.size());
+        for(auto& v : bedpoints)
+            bed.append(Point::new_scale(v(0), v(1)));
+
+        if(pind) pind->update(0, L("Arranging objects..."));
+
+        try {
+            arr::BedShapeHint hint;
+            // TODO: from Sasha from GUI
+            hint.type = arr::BedShapeType::WHO_KNOWS;
+
+//FIXME merge error
+/*
+            arr::arrange(*model_,
+                         min_obj_distance,
+                         bed,
+                         hint,
+                         false, // create many piles not just one pile
+                         [pind, count](unsigned rem) {
+                if(pind)
+                    pind->update(count - rem, L("Arranging objects..."));
+            });
+*/
+        } catch(std::exception& e) {
+            std::cerr << e.what() << std::endl;
+            report_issue(IssueType::ERR,
+                         L("Could not arrange model objects! "
+                         "Some geometries may be invalid."),
+                         L("Exception occurred"));
+        }
+
+        // Restore previous max value
+        if(pind) {
+            pind->max(pmax);
+            pind->update(0, L("Arranging done."));
+        }
+    });
+
+    while( ftr.wait_for(std::chrono::milliseconds(10))
+           != std::future_status::ready) {
+        process_events();
     }
-
-    auto dist = print_ctl()->config().min_object_distance();
-
-    // Create the arranger config
-    auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
-
-    auto& bedpoints = print_ctl()->config().bed_shape.values;
-    Polyline bed; bed.points.reserve(bedpoints.size());
-    for(auto& v : bedpoints)
-        bed.append(Point::new_scale(v(0), v(1)));
-
-    if(pind) pind->update(0, _(L("Arranging objects...")));
-
-    try {
-        arr::BedShapeHint hint;
-        // TODO: from Sasha from GUI
-        hint.type = arr::BedShapeType::WHO_KNOWS;
-
-        arr::arrange(*model_,
-                      min_obj_distance,
-                      bed,
-                      hint,
-                      false, // create many piles not just one pile
-                      [this, pind, count](unsigned rem) {
-            if(pind)
-                pind->update(count - rem, L("Arranging objects..."));
-
-            process_events();
-        }, [this] () { return !arranging_.load(); });
-    } catch(std::exception& e) {
-        std::cerr << e.what() << std::endl;
-        report_issue(IssueType::ERR,
-                        _(L("Could not arrange model objects! "
-                        "Some geometries may be invalid.")),
-                        _(L("Exception occurred")));
-    }
-
-    // Restore previous max value
-    if(pind) {
-        pind->max(pmax);
-        pind->update(0, arranging_.load() ? L("Arranging done.") :
-                                            L("Arranging canceled."));
-
-        pind->on_cancel(/*remove cancel function*/);
-    }
-
-    arranging_.store(false);
 }
 
 }
diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp
index 5a7294f79..8bf1b905a 100644
--- a/xs/src/slic3r/AppController.hpp
+++ b/xs/src/slic3r/AppController.hpp
@@ -16,6 +16,7 @@ class Print;
 class PrintObject;
 class PrintConfig;
 class ProgressStatusBar;
+class DynamicPrintConfig;
 
 /**
  * @brief A boilerplate class for creating application logic. It should provide
@@ -46,7 +47,7 @@ public:
     AppControllerBoilerplate();
     ~AppControllerBoilerplate();
 
-    using Path = string;
+    using Path = std::string;
     using PathList = std::vector<Path>;
 
     /// Common runtime issue types
@@ -67,20 +68,20 @@ public:
      * @return Returns a list of paths choosed by the user.
      */
     PathList query_destination_paths(
-            const string& title,
+            const std::string& title,
             const std::string& extensions) const;
 
     /**
      * @brief Same as query_destination_paths but works for directories only.
      */
     PathList query_destination_dirs(
-            const string& title) const;
+            const std::string& title) const;
 
     /**
      * @brief Same as query_destination_paths but returns only one path.
      */
     Path query_destination_path(
-            const string& title,
+            const std::string& title,
             const std::string& extensions,
             const std::string& hint = "") const;
 
@@ -95,11 +96,11 @@ public:
      * title.
      */
     bool report_issue(IssueType issuetype,
-                      const string& description,
-                      const string& brief);
+                      const std::string& description,
+                      const std::string& brief);
 
     bool report_issue(IssueType issuetype,
-                      const string& description);
+                      const std::string& description);
 
     /**
      * @brief Return the global progress indicator for the current controller.
@@ -150,12 +151,12 @@ protected:
      */
     ProgresIndicatorPtr create_progress_indicator(
             unsigned statenum,
-            const string& title,
-            const string& firstmsg) const;
+            const std::string& title,
+            const std::string& firstmsg) const;
 
     ProgresIndicatorPtr create_progress_indicator(
             unsigned statenum,
-            const string& title) const;
+            const std::string& title) const;
 
     // This is a global progress indicator placeholder. In the Slic3r UI it can
     // contain the progress indicator on the statusbar.
@@ -167,43 +168,6 @@ protected:
  */
 class PrintController: public AppControllerBoilerplate {
     Print *print_ = nullptr;
-protected:
-
-    void make_skirt();
-    void make_brim();
-    void make_wipe_tower();
-
-    void make_perimeters(PrintObject *pobj);
-    void infill(PrintObject *pobj);
-    void gen_support_material(PrintObject *pobj);
-
-    // Data structure with the png export input data
-    struct PngExportData {
-        std::string zippath;                        // output zip file
-        unsigned long width_px = 1440;              // resolution - rows
-        unsigned long height_px = 2560;             // resolution columns
-        double width_mm = 68.0, height_mm = 120.0;  // dimensions in mm
-        double exp_time_first_s = 35.0;             // first exposure time
-        double exp_time_s = 8.0;                    // global exposure time
-        double corr_x = 1.0;                        // offsetting in x
-        double corr_y = 1.0;                        // offsetting in y
-        double corr_z = 1.0;                        // offsetting in y
-    };
-
-    // Should display a dialog with the input fields for printing to png
-    PngExportData query_png_export_data(const DynamicPrintConfig&);
-
-    // The previous export data, to pre-populate the dialog
-    PngExportData prev_expdata_;
-
-    /**
-     * @brief Slice one pront object.
-     * @param pobj The print object.
-     */
-    void slice(PrintObject *pobj);
-
-    void slice(ProgresIndicatorPtr pri);
-
 public:
 
     // Must be public for perl to use it
@@ -218,15 +182,9 @@ public:
         return PrintController::Ptr( new PrintController(print) );
     }
 
-    /**
-     * @brief Slice the loaded print scene.
-     */
-    void slice();
-
-    /**
-     * @brief Slice the print into zipped png files.
-     */
-    void slice_to_png();
+    //FIXME Vojtech: Merging error
+    void slice() {}
+    void slice_to_png() {}
 
     const PrintConfig& config() const;
 };
@@ -237,7 +195,6 @@ public:
 class AppController: public AppControllerBoilerplate {
     Model *model_ = nullptr;
     PrintController::Ptr printctl;
-    std::atomic<bool> arranging_;
 public:
 
     /**
@@ -273,14 +230,15 @@ public:
      * In perl we have a progress indicating status bar on the bottom of the
      * window which is defined and created in perl. We can pass the ID-s of the
      * gauge and the statusbar id and make a wrapper implementation of the
-     * IProgressIndicator interface so we can use this GUI widget from C++.
+     * ProgressIndicator interface so we can use this GUI widget from C++.
      *
      * This function should be called from perl.
      *
      * @param gauge_id The ID of the gague widget of the status bar.
      * @param statusbar_id The ID of the status bar.
      */
-    void set_global_progress_indicator(ProgressStatusBar *prs);
+    void set_global_progress_indicator(unsigned gauge_id,
+                                          unsigned statusbar_id);
 
     void arrange_model();
 };
diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp
index 279b6d787..36a465919 100644
--- a/xs/src/slic3r/AppControllerWx.cpp
+++ b/xs/src/slic3r/AppControllerWx.cpp
@@ -4,7 +4,6 @@
 #include <future>
 
 #include <slic3r/GUI/GUI.hpp>
-#include <slic3r/GUI/ProgressStatusBar.hpp>
 
 #include <wx/app.h>
 #include <wx/filedlg.h>
@@ -28,16 +27,16 @@ bool AppControllerBoilerplate::supports_asynch() const
 
 void AppControllerBoilerplate::process_events()
 {
-    wxYieldIfNeeded();
+    wxSafeYield();
 }
 
 AppControllerBoilerplate::PathList
 AppControllerBoilerplate::query_destination_paths(
-        const string &title,
+        const std::string &title,
         const std::string &extensions) const
 {
 
-    wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+    wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
     dlg.SetWildcard(extensions);
 
     dlg.ShowModal();
@@ -53,11 +52,11 @@ AppControllerBoilerplate::query_destination_paths(
 
 AppControllerBoilerplate::Path
 AppControllerBoilerplate::query_destination_path(
-        const string &title,
+        const std::string &title,
         const std::string &extensions,
         const std::string& hint) const
 {
-    wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+    wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
     dlg.SetWildcard(extensions);
 
     dlg.SetFilename(hint);
@@ -72,8 +71,8 @@ AppControllerBoilerplate::query_destination_path(
 }
 
 bool AppControllerBoilerplate::report_issue(IssueType issuetype,
-                                 const string &description,
-                                 const string &brief)
+                                 const std::string &description,
+                                 const std::string &brief)
 {
     auto icon = wxICON_INFORMATION;
     auto style = wxOK|wxCENTRE;
@@ -85,15 +84,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype,
     case IssueType::FATAL:  icon = wxICON_ERROR;
     }
 
-    auto ret = wxMessageBox(description, brief, icon | style);
+    auto ret = wxMessageBox(_(description), _(brief), icon | style);
     return ret != wxCANCEL;
 }
 
 bool AppControllerBoilerplate::report_issue(
         AppControllerBoilerplate::IssueType issuetype,
-        const string &description)
+        const std::string &description)
 {
-    return report_issue(issuetype, description, string());
+    return report_issue(issuetype, description, std::string());
 }
 
 wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
@@ -137,8 +136,8 @@ public:
     /// Get the mode of parallel operation.
     inline bool asynch() const { return is_asynch_; }
 
-    inline GuiProgressIndicator(int range, const string& title,
-                                const string& firstmsg) :
+    inline GuiProgressIndicator(int range, const wxString& title,
+                                const wxString& firstmsg) :
         gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
                wxPD_APP_MODAL | wxPD_AUTO_HIDE),
         message_(firstmsg),
@@ -152,11 +151,6 @@ public:
              this, id_);
     }
 
-    virtual void cancel() override {
-        update(max(), "Abort");
-        ProgressIndicator::cancel();
-    }
-
     virtual void state(float val) override {
         state(static_cast<unsigned>(val));
     }
@@ -171,26 +165,28 @@ public:
         } else _state(st);
     }
 
-    virtual void message(const string & msg) override {
-        message_ = msg;
+    virtual void message(const std::string & msg) override {
+        message_ = _(msg);
     }
 
-    virtual void messageFmt(const string& fmt, ...) {
+    virtual void messageFmt(const std::string& fmt, ...) {
         va_list arglist;
         va_start(arglist, fmt);
-        message_ = wxString::Format(wxString(fmt), arglist);
+        message_ = wxString::Format(_(fmt), arglist);
         va_end(arglist);
     }
 
-    virtual void title(const string & title) override {
-        title_ = title;
+    virtual void title(const std::string & title) override {
+        title_ = _(title);
     }
 };
 }
 
 AppControllerBoilerplate::ProgresIndicatorPtr
 AppControllerBoilerplate::create_progress_indicator(
-        unsigned statenum, const string& title, const string& firstmsg) const
+        unsigned statenum,
+        const std::string& title,
+        const std::string& firstmsg) const
 {
     auto pri =
             std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
@@ -203,29 +199,39 @@ AppControllerBoilerplate::create_progress_indicator(
 }
 
 AppControllerBoilerplate::ProgresIndicatorPtr
-AppControllerBoilerplate::create_progress_indicator(unsigned statenum,
-                                                    const string &title) const
+AppControllerBoilerplate::create_progress_indicator(
+        unsigned statenum, const std::string &title) const
 {
-    return create_progress_indicator(statenum, title, string());
+    return create_progress_indicator(statenum, title, std::string());
 }
 
 namespace {
 
+// A wrapper progress indicator class around the statusbar created in perl.
 class Wrapper: public ProgressIndicator, public wxEvtHandler {
-    ProgressStatusBar *sbar_;
+    wxGauge *gauge_;
+    wxStatusBar *stbar_;
     using Base = ProgressIndicator;
-    std::string message_;
+    wxString message_;
     AppControllerBoilerplate& ctl_;
 
     void showProgress(bool show = true) {
-        sbar_->show_progress(show);
+        gauge_->Show(show);
     }
 
     void _state(unsigned st) {
         if( st <= ProgressIndicator::max() ) {
             Base::state(st);
-            sbar_->set_status_text(message_);
-            sbar_->set_progress(st);
+
+            if(!gauge_->IsShown()) showProgress(true);
+
+            stbar_->SetStatusText(message_);
+            if(static_cast<long>(st) == gauge_->GetRange()) {
+                gauge_->SetValue(0);
+                showProgress(false);
+            } else {
+                gauge_->SetValue(static_cast<int>(st));
+            }
         }
     }
 
@@ -238,12 +244,12 @@ class Wrapper: public ProgressIndicator, public wxEvtHandler {
 
 public:
 
-    inline Wrapper(ProgressStatusBar *sbar,
+    inline Wrapper(wxGauge *gauge, wxStatusBar *stbar,
                    AppControllerBoilerplate& ctl):
-        sbar_(sbar), ctl_(ctl)
+        gauge_(gauge), stbar_(stbar), ctl_(ctl)
     {
-        Base::max(static_cast<float>(sbar_->get_range()));
-        Base::states(static_cast<unsigned>(sbar_->get_range()));
+        Base::max(static_cast<float>(gauge->GetRange()));
+        Base::states(static_cast<unsigned>(gauge->GetRange()));
 
         Bind(PROGRESS_STATUS_UPDATE_EVENT,
              &Wrapper::_state,
@@ -256,7 +262,7 @@ public:
 
     virtual void max(float val) override {
         if(val > 1.0) {
-            sbar_->set_range(static_cast<int>(val));
+            gauge_->SetRange(static_cast<int>(val));
             ProgressIndicator::max(val);
         }
     }
@@ -271,32 +277,31 @@ public:
         }
     }
 
-    virtual void message(const string & msg) override {
-        message_ = msg;
+    virtual void message(const std::string & msg) override {
+        message_ = _(msg);
     }
 
-    virtual void message_fmt(const string& fmt, ...) override {
+    virtual void message_fmt(const std::string& fmt, ...) override {
         va_list arglist;
         va_start(arglist, fmt);
-        message_ = wxString::Format(fmt, arglist);
+        message_ = wxString::Format(_(fmt), arglist);
         va_end(arglist);
     }
 
-    virtual void title(const string & /*title*/) override {}
-
-    virtual void on_cancel(CancelFn fn) override {
-        sbar_->set_cancel_callback(fn);
-        Base::on_cancel(fn);
-    }
+    virtual void title(const std::string & /*title*/) override {}
 
 };
 }
 
-void AppController::set_global_progress_indicator(ProgressStatusBar *prsb)
+void AppController::set_global_progress_indicator(
+        unsigned gid,
+        unsigned sid)
 {
-    if(prsb) {
-        global_progress_indicator(std::make_shared<Wrapper>(prsb, *this));
+    wxGauge* gauge = dynamic_cast<wxGauge*>(wxWindow::FindWindowById(gid));
+    wxStatusBar* sb = dynamic_cast<wxStatusBar*>(wxWindow::FindWindowById(sid));
+
+    if(gauge && sb) {
+        global_progressind_ = std::make_shared<Wrapper>(gauge, sb, *this);
     }
 }
-
 }
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index f755fdb03..952fe07e0 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -648,7 +648,7 @@ std::vector<int> GLVolumeCollection::load_object(
         const ModelVolume *model_volume = model_object->volumes[volume_idx];
 
         int extruder_id = -1;
-        if (!model_volume->modifier)
+        if (model_volume->is_model_part())
         {
             extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
             if (extruder_id == 0)
@@ -661,7 +661,16 @@ std::vector<int> GLVolumeCollection::load_object(
             volumes_idx.push_back(int(this->volumes.size()));
             float color[4];
             memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
-            color[3] = model_volume->modifier ? 0.5f : 1.f;
+            if (model_volume->is_support_blocker()) {
+                color[0] = 1.0f;
+                color[1] = 0.2f;
+                color[2] = 0.2f;
+            } else if (model_volume->is_support_enforcer()) {
+                color[0] = 0.2f;
+                color[1] = 0.2f;
+                color[2] = 1.0f;
+            }
+            color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
             this->volumes.emplace_back(new GLVolume(color));
             GLVolume &v = *this->volumes.back();
             if (use_VBOs)
@@ -675,15 +684,15 @@ std::vector<int> GLVolumeCollection::load_object(
             v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx;
             v.set_select_group_id(select_by);
             v.set_drag_group_id(drag_by);
-            if (!model_volume->modifier)
+            if (model_volume->is_model_part())
             {
                 v.set_convex_hull(model_volume->get_convex_hull());
                 v.layer_height_texture = layer_height_texture;
                 if (extruder_id != -1)
                     v.extruder_id = extruder_id;
             }
-            v.is_modifier = model_volume->modifier;
-            v.shader_outside_printer_detection_enabled = !model_volume->modifier;
+            v.is_modifier = ! model_volume->is_model_part();
+            v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
 #if ENABLE_MODELINSTANCE_3D_OFFSET
             v.set_offset(instance->get_offset());
 #else
diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp
index 0f77b1953..d7307cc32 100644
--- a/xs/src/slic3r/GUI/AppConfig.cpp
+++ b/xs/src/slic3r/GUI/AppConfig.cpp
@@ -60,6 +60,14 @@ void AppConfig::set_defaults()
 
     if (get("remember_output_path").empty())
         set("remember_output_path", "1");
+
+    // Remove legacy window positions/sizes
+    erase("", "main_frame_maximized");
+    erase("", "main_frame_pos");
+    erase("", "main_frame_size");
+    erase("", "object_settings_maximized");
+    erase("", "object_settings_pos");
+    erase("", "object_settings_size");
 }
 
 void AppConfig::load()
diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp
index b742176ed..5af635a12 100644
--- a/xs/src/slic3r/GUI/AppConfig.hpp
+++ b/xs/src/slic3r/GUI/AppConfig.hpp
@@ -72,6 +72,14 @@ public:
 	bool				has(const std::string &key) const
 		{ return this->has("", key); }
 
+	void				erase(const std::string &section, const std::string &key)
+	{
+		auto it = m_storage.find(section);
+		if (it != m_storage.end()) {
+			it->second.erase(key);
+		}
+	}
+
 	void 				clear_section(const std::string &section)
 		{ m_storage[section].clear(); }
 
diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp
index 9f736ded8..e784d8525 100644
--- a/xs/src/slic3r/GUI/ConfigWizard.cpp
+++ b/xs/src/slic3r/GUI/ConfigWizard.cpp
@@ -409,11 +409,10 @@ PageFirmware::PageFirmware(ConfigWizard *parent) :
 
 void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
 {
-	ConfigOptionEnum<GCodeFlavor> opt;
-
 	auto sel = gcode_picker->GetSelection();
-	if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) {
-		config.set_key_value("gcode_flavor", &opt);
+	if (sel >= 0 && sel < gcode_opt.enum_labels.size()) {
+		auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
+		config.set_key_value("gcode_flavor", opt);
 	}
 }
 
@@ -871,10 +870,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
 	// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
 	auto wizard_size = GetSize();
 	unsigned width, height;
-	GUI::get_current_screen_size(width, height);
-	wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
-	wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
-	SetMinSize(wizard_size);
+	if (GUI::get_current_screen_size(this, width, height)) {
+		wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
+		wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
+		SetMinSize(wizard_size);
+	}
 	Fit();
 
 	p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index d0cd9f8cf..d5ac64d90 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -367,7 +367,7 @@ void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries)
 
 		auto ports = Utils::scan_serial_ports_extended();
 		ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
-			return port.id_vendor != USB_VID_PRUSA && port.id_product != USB_PID_MMU_BOOT;
+			return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT;
 		}), ports.end());
 
 		if (ports.size() == 1) {
@@ -390,23 +390,22 @@ void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port)
 
 void FirmwareDialog::priv::lookup_port_mmu()
 {
+	static const auto msg_not_found =
+		"The Multi Material Control device was not found.\n"
+		"If the device is connected, please press the Reset button next to the USB connector ...";
+
 	BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
 
 	auto ports = Utils::scan_serial_ports_extended();
 	ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
-		return port.id_vendor != USB_VID_PRUSA &&
+		return port.id_vendor != USB_VID_PRUSA ||
 			port.id_product != USB_PID_MMU_BOOT &&
 			port.id_product != USB_PID_MMU_APP;
 	}), ports.end());
 
 	if (ports.size() == 0) {
 		BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
-
-		queue_status(_(L(
-			"The Multi Material Control device was not found.\n"
-			"If the device is connected, please press the Reset button next to the USB connector ..."
-		)));
-
+		queue_status(_(L(msg_not_found)));
 		wait_for_mmu_bootloader(30);
 	} else if (ports.size() > 1) {
 		BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
@@ -417,6 +416,13 @@ void FirmwareDialog::priv::lookup_port_mmu()
 			BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
 			mmu_reboot(ports[0]);
 			wait_for_mmu_bootloader(10);
+
+			if (! port) {
+				// The device in bootloader mode was not found, inform the user and wait some more...
+				BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ...";
+				queue_status(_(L(msg_not_found)));
+				wait_for_mmu_bootloader(30);
+			}
 		} else {
 			port = ports[0];
 		}
@@ -702,7 +708,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
 	panel->SetSizer(vsizer);
 
 	auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
-	p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
+	p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr, 
+		"Hex files (*.hex)|*.hex|All files|*.*");
 
 	auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
 	p->port_picker = new wxComboBox(panel, wxID_ANY);
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 433acea1d..dac288188 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -7,6 +7,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
 
 #if __APPLE__
 #import <IOKit/pwr_mgt/IOPMLib.h>
@@ -1226,12 +1227,63 @@ int get_export_option(wxFileDialog* dlg)
 
 }
 
-void get_current_screen_size(unsigned &width, unsigned &height)
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
 {
-	wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame));
+	const auto idx = wxDisplay::GetFromWindow(window);
+	if (idx == wxNOT_FOUND) {
+		return false;
+	}
+
+	wxDisplay display(idx);
 	const auto disp_size = display.GetClientArea();
 	width = disp_size.GetWidth();
 	height = disp_size.GetHeight();
+
+	return true;
+}
+
+void save_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+	const wxSize size = window->GetSize();
+	const wxPoint pos = window->GetPosition();
+	const auto maximized = window->IsMaximized() ? "1" : "0";
+
+	g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
+	g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized);
+}
+
+void restore_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+	// XXX: This still doesn't behave nicely in some situations (mostly on Linux).
+	// The problem is that it's hard to obtain window position with respect to screen geometry reliably
+	// from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
+	// it's actually visible. I suspect this has something to do with window initialization (maybe we
+	// restore window geometry too early), but haven't yet found a workaround.
+
+	const auto display_idx = wxDisplay::GetFromWindow(window);
+	if (display_idx == wxNOT_FOUND) { return; }
+
+	const auto display = wxDisplay(display_idx).GetClientArea();
+	std::vector<std::string> pair;
+
+	try {
+		const auto key_size = (boost::format("window_%1%_size") % name).str();
+		if (g_AppConfig->has(key_size)) {
+			if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) {
+				auto width = boost::lexical_cast<int>(pair[0]);
+				auto height = boost::lexical_cast<int>(pair[1]);
+
+				window->SetSize(width, height);
+			}
+		}
+	} catch(const boost::bad_lexical_cast &) {}
+
+	// Maximizing should be the last thing to do.
+	// This ensure the size and position are sane when the user un-maximizes the window.
+	const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
+	if (g_AppConfig->get(key_maximized) == "1") {
+		window->Maximize(true);
+	}
 }
 
 void enable_action_buttons(bool enable)
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 448c37773..ebdaeb5c9 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -26,6 +26,7 @@ class wxButton;
 class wxFileDialog;
 class wxStaticBitmap;
 class wxFont;
+class wxTopLevelWindow;
 
 namespace Slic3r { 
 
@@ -223,7 +224,12 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
 int get_export_option(wxFileDialog* dlg);
 
 // Returns the dimensions of the screen on which the main frame is displayed
-void get_current_screen_size(unsigned &width, unsigned &height);
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
+
+// Save window size and maximized status into AppConfig
+void save_window_size(wxTopLevelWindow *window, const std::string &name);
+// Restore the above
+void restore_window_size(wxTopLevelWindow *window, const std::string &name);
 
 // Update buttons view according to enable/disable
 void enable_action_buttons(bool enable);
diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
index eacdd3b52..44c32dfd3 100644
--- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
+++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
@@ -1225,7 +1225,7 @@ void load_part(	ModelObject* model_object,
 		for ( auto object : model.objects) {
 			for (auto volume : object->volumes) {
 				auto new_volume = model_object->add_volume(*volume);
-				new_volume->modifier = is_modifier;
+				new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
 				boost::filesystem::path(input_file).filename().string();
 				new_volume->name = boost::filesystem::path(input_file).filename().string();
 
@@ -1283,7 +1283,8 @@ void load_lambda(	ModelObject* model_object,
 	mesh.repair();
 
 	auto new_volume = model_object->add_volume(mesh);
-	new_volume->modifier = is_modifier;
+	new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+
 	new_volume->name = name;
 	// set a default extruder value, since user can't add it manually
 	new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
@@ -1320,7 +1321,8 @@ void load_lambda(const std::string& type_name)
     mesh.repair();
 
     auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh);
-    new_volume->modifier = true;
+    new_volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+
     new_volume->name = name;
     // set a default extruder value, since user can't add it manually
     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
@@ -1385,9 +1387,9 @@ bool remove_subobject_from_object(const int volume_id)
     // if user is deleting the last solid part, throw error
     int solid_cnt = 0;
     for (auto vol : (*m_objects)[m_selected_object_id]->volumes)
-        if (!vol->modifier)
+        if (vol->is_model_part())
             ++solid_cnt;
-    if (!volume->modifier && solid_cnt == 1) {
+    if (volume->is_model_part() && solid_cnt == 1) {
         Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object.")));
         return false;
     }
@@ -1477,7 +1479,7 @@ void on_btn_split(const bool split_part)
 
         for (auto id = 0; id < model_object->volumes.size(); id++)
             m_objects_model->AddChild(parent, model_object->volumes[id]->name,
-                                      model_object->volumes[id]->modifier ? m_icon_modifiermesh : m_icon_solidmesh,
+                                      model_object->volumes[id]->is_modifier() ? m_icon_modifiermesh : m_icon_solidmesh,
                                       model_object->volumes[id]->config.has("extruder") ?
                                         model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
                                       false);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 81ba9790f..fca79e683 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -295,7 +295,7 @@ const std::vector<std::string>& Preset::print_options()
         "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
         "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", 
         "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
-        "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", 
+        "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", 
         "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", 
         "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", 
         "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 5daf2784d..00a04a713 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -880,6 +880,7 @@ void TabPrint::build()
 	page = add_options_page(_(L("Support material")), "building.png");
 		optgroup = page->new_optgroup(_(L("Support material")));
 		optgroup->append_single_option_line("support_material");
+		optgroup->append_single_option_line("support_material_auto");
 		optgroup->append_single_option_line("support_material_threshold");
 		optgroup->append_single_option_line("support_material_enforce_layers");
 
@@ -1219,13 +1220,15 @@ void TabPrint::update()
 
 	bool have_raft = m_config->opt_int("raft_layers") > 0;
 	bool have_support_material = m_config->opt_bool("support_material") || have_raft;
+	bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");
 	bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
 	bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
-	for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
+	for (auto el : {"support_material_pattern", "support_material_with_sheath",
 					"support_material_spacing", "support_material_angle", "support_material_interface_layers",
 					"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
 					"support_material_xy_spacing" })
 		get_field(el)->toggle(have_support_material);
+	get_field("support_material_threshold")->toggle(have_support_material_auto);
 
 	for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
 					"support_material_interface_speed", "support_material_interface_contact_loops" })
diff --git a/xs/src/slic3r/ProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp
index 4a39e84ab..0cf8b4a17 100644
--- a/xs/src/slic3r/ProgressIndicator.hpp
+++ b/xs/src/slic3r/ProgressIndicator.hpp
@@ -3,7 +3,6 @@
 
 #include <string>
 #include <functional>
-#include "Strings.hpp"
 
 namespace Slic3r {
 
@@ -43,13 +42,13 @@ public:
     }
 
     /// Message shown on the next status update.
-    virtual void message(const string&) = 0;
+    virtual void message(const std::string&) = 0;
 
     /// Title of the operation.
-    virtual void title(const string&) = 0;
+    virtual void title(const std::string&) = 0;
 
     /// Formatted message for the next status update. Works just like sprintf.
-    virtual void message_fmt(const string& fmt, ...);
+    virtual void message_fmt(const std::string& fmt, ...);
 
     /// Set up a cancel callback for the operation if feasible.
     virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
@@ -61,7 +60,7 @@ public:
     virtual void cancel() { cancelfunc_(); }
 
     /// Convenience function to call message and status update in one function.
-    void update(float st, const string& msg) {
+    void update(float st, const std::string& msg) {
         message(msg); state(st);
     }
 };
diff --git a/xs/src/slic3r/Strings.hpp b/xs/src/slic3r/Strings.hpp
deleted file mode 100644
index b267fe064..000000000
--- a/xs/src/slic3r/Strings.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef STRINGS_HPP
-#define STRINGS_HPP
-
-#include "GUI/GUI.hpp"
-
-namespace Slic3r {
-using string = wxString;
-}
-
-#endif // STRINGS_HPP
diff --git a/xs/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp
index 183119b44..601719b50 100644
--- a/xs/src/slic3r/Utils/Serial.cpp
+++ b/xs/src/slic3r/Utils/Serial.cpp
@@ -231,7 +231,12 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
                 spi.port = path;
 #ifdef __linux__
 				auto friendly_name = sysfs_tty_prop(name, "product");
-				spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path;
+				if (friendly_name) {
+					spi.is_printer = looks_like_printer(*friendly_name);
+					spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str();
+				} else {
+					spi.friendly_name = path;
+				}
 				auto vid = sysfs_tty_prop_hex(name, "idVendor");
 				auto pid = sysfs_tty_prop_hex(name, "idProduct");
 				if (vid && pid) {
diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp
index 8156b0ad2..383c316ad 100644
--- a/xs/xsp/AppController.xsp
+++ b/xs/xsp/AppController.xsp
@@ -23,7 +23,7 @@
     PrintController *print_ctl();
     void set_model(Model *model);
     void set_print(Print *print);
-    void set_global_progress_indicator(ProgressStatusBar *prs);
+    void set_global_progress_indicator(unsigned gauge_id, unsigned statusbar_id);
 
     void arrange_model();
 };
\ No newline at end of file
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index c19285d3e..813eb5e33 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -186,3 +186,8 @@ void reset_double_slider()
 void enable_action_buttons(bool enable)
     %code%{ Slic3r::GUI::enable_action_buttons(enable); %};
 
+void save_window_size(SV *window, std::string name)
+    %code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
+
+void restore_window_size(SV *window, std::string name)
+    %code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index 4f09fb521..efd6c9ae6 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -17,8 +17,6 @@
         %code%{ RETVAL = &THIS->thin_fills; %};
     Ref<SurfaceCollection> fill_surfaces()
         %code%{ RETVAL = &THIS->fill_surfaces; %};
-    Ref<SurfaceCollection> perimeter_surfaces()
-        %code%{ RETVAL = &THIS->perimeter_surfaces; %};
     Polygons bridged()
         %code%{ RETVAL = THIS->bridged; %};
     Ref<PolylineCollection> unsupported_bridge_edges()
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 6ae7d6415..101faf564 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -340,9 +340,19 @@ ModelMaterial::attributes()
         %code%{ RETVAL = &THIS->mesh; %};
     
     bool modifier()
-        %code%{ RETVAL = THIS->modifier; %};
+        %code%{ RETVAL = THIS->is_modifier(); %};
     void set_modifier(bool modifier)
-        %code%{ THIS->modifier = modifier; %};
+        %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %};
+    bool model_part()
+        %code%{ RETVAL = THIS->is_model_part(); %};
+    bool support_enforcer()
+        %code%{ RETVAL = THIS->is_support_enforcer(); %};
+    void set_support_enforcer()
+        %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %};
+    bool support_blocker()
+        %code%{ RETVAL = THIS->is_support_blocker(); %};
+    void set_support_blocker()
+        %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
 
     size_t split(unsigned int max_extruders);
     
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index 96004d1a5..fbf48e742 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -267,6 +267,36 @@ Print::total_cost(...)
             THIS->total_cost = (double)SvNV(ST(1));
         }
         RETVAL = THIS->total_cost;
+    OUTPUT:
+        RETVAL
+
+double
+Print::total_wipe_tower_cost(...)
+    CODE:
+        if (items > 1) {
+            THIS->total_wipe_tower_cost = (double)SvNV(ST(1));
+        }
+        RETVAL = THIS->total_wipe_tower_cost;
+    OUTPUT:
+        RETVAL
+
+double
+Print::total_wipe_tower_filament(...)
+    CODE:
+        if (items > 1) {
+            THIS->total_wipe_tower_filament = (double)SvNV(ST(1));
+        }
+        RETVAL = THIS->total_wipe_tower_filament;
+    OUTPUT:
+        RETVAL
+
+int
+Print::m_wipe_tower_number_of_toolchanges(...)
+    CODE:
+        if (items > 1) {
+            THIS->m_wipe_tower_number_of_toolchanges = (double)SvNV(ST(1));
+        }
+        RETVAL = THIS->m_wipe_tower_number_of_toolchanges;
     OUTPUT:
         RETVAL        
 %}
diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp
index e900532aa..04969a7f9 100644
--- a/xs/xsp/XS.xsp
+++ b/xs/xsp/XS.xsp
@@ -48,6 +48,11 @@ trace(level, message)
     CODE:
         Slic3r::trace(level, message);
 
+void
+disable_multi_threading()
+    CODE:
+        Slic3r::disable_multi_threading();
+
 void
 set_var_dir(dir)
     char  *dir;