From 2a81408e8bd64bd3f28319b418ab933895b301d9 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 6 Sep 2018 14:19:20 +0200
Subject: [PATCH] Implemented support enforcers / blockers. Reduced amount of
 full support interfaces similar to S3D.

---
 lib/Slic3r/GUI/Plater/ObjectCutDialog.pm      |   2 +-
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm     |  79 ++++--
 .../GUI/Plater/OverrideSettingsPanel.pm       |  50 +++-
 resources/icons/support_blocker.png           | Bin 0 -> 656 bytes
 resources/icons/support_enforcer.png          | Bin 0 -> 509 bytes
 xs/src/libslic3r/EdgeGrid.cpp                 |   2 +
 xs/src/libslic3r/Format/3mf.cpp               |  12 +-
 xs/src/libslic3r/Format/AMF.cpp               |  14 +-
 xs/src/libslic3r/Model.cpp                    |  51 +++-
 xs/src/libslic3r/Model.hpp                    |  44 +++-
 xs/src/libslic3r/Print.cpp                    |  10 +-
 xs/src/libslic3r/Print.hpp                    |   5 +
 xs/src/libslic3r/PrintConfig.cpp              |   2 +-
 xs/src/libslic3r/PrintObject.cpp              |  77 ++++--
 xs/src/libslic3r/PrintRegion.cpp              |   2 +-
 xs/src/libslic3r/Slicing.cpp                  |   6 +-
 xs/src/libslic3r/SupportMaterial.cpp          | 234 ++++++++++++------
 xs/src/libslic3r/SupportMaterial.hpp          |   1 +
 xs/src/slic3r/GUI/3DScene.cpp                 |  19 +-
 xs/xsp/Model.xsp                              |  14 +-
 20 files changed, 461 insertions(+), 163 deletions(-)
 create mode 100644 resources/icons/support_blocker.png
 create mode 100644 resources/icons/support_enforcer.png

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 783c1a9f5..7e9ee05dd 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/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index 3b10ed99f..a64bff393 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 0000000000000000000000000000000000000000..8a66c73f4f6aff7721a84c2ce768d78cf0aac128
GIT binary patch
literal 656
zcmV;B0&o3^P)<h;3K|Lk000e1NJLTq000mG000mO0ssI2kdbIM00006bW%=J00000
z003^L2ax~(010qNS#tmY4#WTe4#WYKD-Ig~00J;cL_t(2k&RMaNRwd{e!hL*T(veE
zv$B}P+}aw;iwbqi1%qykmQYa?SrCyDqrxr<k-|W!n^8p6pi3np3WP8yNVmdTZCF&8
zZ2plZZPUh@e(dLczqgCe+`t>3tMfkRIh^O5_aHwQOgY^tr#t)fIa>v|l+!Iu&j8?5
zD&5$3|LEPgI4e<R(pb*yIFA)js7{@bBbLi$e7>X@x30IEwdTq6#7Z>)zN`NQ%Vfy#
z7XVTy!ZBoaS1;y8HLt0ybQA(_#j70v1iMWx;22}K!q*zRhPDmIWh#2wInrDgO~xe3
zE?yc?r*pE~B})ME0;1i`$y%dKS$aNfe-bh3jGk9Xbvh@zEzSUdQd&?d!ktaiyDDYY
z*7N@0$wxaMN93of$+KL*F~&MVZIn{2vV4nD?>YLAFG!_Rj@k9T;}uy@LTG=>YXyLA
z$DP{^_Yw<<g1~)sZY)*Ln#zYd5A3O_-&^A#lq^}2K?vOxnY2abi`{{(<LM*bKGQ&q
zND?6wfB@j~n-)T7LpbB;4V8&xS<L_dn#6+39~v3|L2YFKxcp%7XPZ|ymo0TN+GzP(
zp|0!wT<rFLv;39l)SBOaO4Y&o4G;FOo2u5v&bbOoMb)=B08px^G6;5C@iYKTxQrKW
zS`R-9_nmz?6aOVK>Ej`Df#>9`*nR_mu-4n!x_N*QLMSDKC>SrNDU|}B-U*!2uup$1
qFjxG()tt0=-#>3KFScK2jQs;LJU(a{EOJ%=0000<MNUMnLSTX)aU}i#

literal 0
HcmV?d00001

diff --git a/resources/icons/support_enforcer.png b/resources/icons/support_enforcer.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0bef8d5e14368d80a3b1e5bfc288d986df38504
GIT binary patch
literal 509
zcmV<Z0RsMsP)<h;3K|Lk000e1NJLTq000mG000mO0ssI2kdbIM00006bW%=J00000
z003^L2ax~(010qNS#tmY4#WTe4#WYKD-Ig~00EszL_t(2k(H9cOVe=}#-E=PS><T9
znHMXDq&UO=fo>7xp;PyQ4hxbfI(F{TE$onlr!JkMLv%4_b<9)@c8Cc-L1UY_VnnU<
z`##UxVG_-m(tCS(`Ml4|hc|L}{7-f7>Zd<G*l1QnA4&~h9W8XOYXg3=x!K$p=^Y7f
zaU(uB;ZWi*gy7-FcJp|0;Ge*~4|gS!qA0pMdJ;Y5uPXrVyt-j#QxlKd0E)v$PRk{C
zhbXxt*Ovn@)SsJ=7k4L+q)V>K_qE2ahPlbTD@p(WA~Wj%jHP<%{<(*p7aN%A&0KzR
z$;{07vM?L{5qEp;dcHqj`m#((PIJv%c)Fh@Gty}!opjL&V6u4aMCuep2j=GA6mP!m
z25_rbFk?EIrW8ETCD9}RgQ=6{wUz2-ZEJgr<W7Pa#|B0TU;P&6kBzr<$pt*8v|jr(
zJNdfpq+n*?^5>;9*|TwNwNkH=%pT3QjPGH|f>9dFj2l&{SD#&Z0pQ}J^8f;aksW~9
z`|^U3X4d5`{U8P4-O@rj+4naP?epGIw8!)}S~4KOas1W>00000NkvXXu0mjf^^oOe

literal 0
HcmV?d00001

diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 733ff2ad7..804b526c7 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1227,6 +1227,8 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
 
 Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) 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;
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 945bb1f86..fd911bf4e 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[] =
@@ -1501,7 +1502,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);
             }
@@ -2017,9 +2020,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 886bbae97..5aa922f62 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -458,9 +458,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) {
@@ -781,8 +786,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/Model.cpp b/xs/src/libslic3r/Model.cpp
index 09b515c2f..e2e84dfd4 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -609,7 +609,7 @@ 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())
                 raw_bbox.merge(v->mesh.bounding_box());
         BoundingBoxf3 bb;
         for (const ModelInstance *i : this->instances)
@@ -640,7 +640,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;
 }
@@ -651,7 +651,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));
         }
@@ -663,7 +663,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;
 }
@@ -674,7 +674,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());
     
     // first align to origin on XYZ
@@ -778,7 +778,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;
 }
@@ -786,7 +786,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;
 }
@@ -802,7 +802,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);
@@ -854,7 +854,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);
@@ -868,7 +868,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)
             {
@@ -972,6 +972,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 dadd515de..34a7c7cc1 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -165,15 +165,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.
@@ -184,24 +196,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/Print.cpp b/xs/src/libslic3r/Print.cpp
index bd14837d9..830c857c9 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -362,9 +362,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)
@@ -379,6 +382,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.
@@ -853,7 +857,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);
     }
 }
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index b16341732..a0cb83e09 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -214,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;
@@ -225,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;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index f9f0b2056..1a8b19c3f 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1734,7 +1734,7 @@ PrintConfigDef::PrintConfigDef()
                    "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/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 7150ead59..6dd764ae1 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -1320,29 +1320,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(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
-                // 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(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
+            // 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 71fcc876a..5bb1fffb3 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -59,7 +59,7 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
 
 coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
 {
-    return this->nozzle_dmr_avg(print_config) * this->config.bridge_flow_ratio.value;
+    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 e9295d1e3..d3fbcc7cb 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 1bf9675df..e1a8995a6 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -283,7 +283,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
         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, 0., 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)
@@ -428,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;
 }
 
@@ -460,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;
 }
 
@@ -469,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),
@@ -493,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);
     }
 
@@ -504,19 +498,19 @@ public:
     Polygons extract_support(const coord_t offset_in_grid)
     {
         // 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_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(), bbox.min - Point(1, 1));
             auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), 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));
@@ -577,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.
@@ -599,7 +595,10 @@ private:
         double coef = 20. / sqrt(l2);
         return Point(p2.x + coef * v.x, p2.y + coef * v.y);
     }
+#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;
@@ -637,6 +636,8 @@ 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;
 };
 
@@ -804,6 +805,10 @@ 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;
 
@@ -841,10 +846,12 @@ 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;
+    // 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, 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];
@@ -855,6 +862,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.
@@ -869,6 +879,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.
@@ -881,7 +892,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);
@@ -893,26 +903,58 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         } else {
                             // 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,
-                                     offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
-                                -0.1f*fw, +0.1f*fw);
+                                     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())
-                                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 (! 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 (! 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, 
@@ -939,7 +981,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 
@@ -948,12 +992,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), - scale_(- no_interface_offset * 0.5f), slices_margin_offset + scale_(- 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]);
@@ -1028,7 +1075,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                     // 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) {
+                                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;
@@ -1051,20 +1098,38 @@ 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.
+                    // 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));
+                    // 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);
+                    } else  {
+                        //Polygons dense_interface_polygons = diff(overhang_polygons, offset(lower_layer_polygons, scale_(no_interface_offset * 0.7f)));
+                        Polygons dense_interface_polygons = diff(overhang_polygons, 
+                            offset2(lower_layer_polygons, scale_(- no_interface_offset * 0.5f), scale_(no_interface_offset * (0.7f + 0.5f)), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                        if (! dense_interface_polygons.empty()) {
+                            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);
+                        }
+                    }
 
                     // 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 * 2] = &new_layer;
                     if (bridging_layer != nullptr) {
@@ -1085,11 +1150,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     // 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 k = 0;
-        for (int i = 0; i < int(contact_out.size()); ++ k) {
+		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;
+            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].
@@ -1148,7 +1238,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             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 - EPSILON; -- contact_idx) {
-                auto *l = top_contacts[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.
@@ -1156,7 +1245,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.
@@ -1168,9 +1258,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
@@ -1206,24 +1296,26 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                                 // 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);
@@ -1247,10 +1339,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
@@ -1303,7 +1396,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;
@@ -1341,7 +1434,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, 0., 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;
@@ -1658,9 +1755,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;
 
@@ -1679,12 +1773,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;
@@ -1764,7 +1856,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
     ++ iRun;
 #endif /* SLIC3R_DEBUG */
 
-    trim_support_layers_by_object(object, intermediate_layers, 0., 0., 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(
@@ -1809,7 +1904,7 @@ 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.
@@ -1958,11 +2053,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);
@@ -2517,7 +2613,7 @@ void modulate_extrusion_by_overlapping_layers(
                 (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
         }
     private:
-        ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&);
+        ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
         const std::vector<ExtrusionPathFragment> &m_path_fragments;
     };
     const coord_t search_radius = 7;
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index da11cd82c..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.
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 705347094..51e749a58 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -622,7 +622,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)
@@ -635,7 +635,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)
@@ -658,15 +667,15 @@ std::vector<int> GLVolumeCollection::load_object(
             else if (drag_by == "instance")
                 v.drag_group_id = obj_idx * 1000 + instance_idx;
 
-            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();
             v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
             v.set_angle_z(instance->rotation);
             v.set_scale_factor(instance->scaling_factor);
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 182963257..0f9b5cd15 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);