From 1e7a3216ca87d9fdeaa5b12f813e5900a77d49bc Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Mon, 8 May 2023 09:19:31 +0200
Subject: [PATCH 1/3] WIP Organic supports intefaces: Further refactor of FDM
 support code - extracted interface routine to common. Implemented support for
 soluble interfaces & half soluble / half non-soluble interfaces.

---
 src/libslic3r/Support/SupportCommon.cpp     | 189 +++++++++
 src/libslic3r/Support/SupportCommon.hpp     |  17 +
 src/libslic3r/Support/SupportMaterial.cpp   | 169 +-------
 src/libslic3r/Support/SupportMaterial.hpp   |  10 -
 src/libslic3r/Support/SupportParameters.cpp |  28 +-
 src/libslic3r/Support/SupportParameters.hpp |  24 ++
 src/libslic3r/Support/TreeSupport.cpp       | 405 ++++++++++++--------
 src/libslic3r/Support/TreeSupport.hpp       |   4 -
 8 files changed, 507 insertions(+), 339 deletions(-)

diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp
index f76ebaa0e..4978b9e40 100644
--- a/src/libslic3r/Support/SupportCommon.cpp
+++ b/src/libslic3r/Support/SupportCommon.cpp
@@ -3,6 +3,7 @@
 #include "../Layer.hpp"
 #include "../Print.hpp"
 #include "../Fill/FillBase.hpp"
+#include "../MutablePolygon.hpp"
 #include "../Geometry.hpp"
 #include "../Point.hpp"
 
@@ -118,6 +119,194 @@ void remove_bridges_from_contacts(
     #endif /* SLIC3R_DEBUG */
 }
 
+// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers.
+std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
+    const PrintObjectConfig           &config,
+    const SupportParameters           &support_params,
+    const SupportGeneratorLayersPtr   &bottom_contacts,
+    const SupportGeneratorLayersPtr   &top_contacts,
+    // Input / output, will be merged with output. Only provided for Organic supports.
+    SupportGeneratorLayersPtr         &top_interface_layers,
+    SupportGeneratorLayersPtr         &top_base_interface_layers,
+    // Input, will be trimmed with the newly created interface layers.
+    SupportGeneratorLayersPtr         &intermediate_layers,
+    SupportGeneratorLayerStorage      &layer_storage)
+{
+    std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> base_and_interface_layers;
+
+    if (! intermediate_layers.empty() && support_params.has_interfaces()) {
+        // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
+        BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
+        const bool snug_supports = config.support_material_style.value != smsGrid;
+        SupportGeneratorLayersPtr &interface_layers       = base_and_interface_layers.first;
+        SupportGeneratorLayersPtr &base_interface_layers  = base_and_interface_layers.second;
+        interface_layers.assign(intermediate_layers.size(), nullptr);
+        if (support_params.has_base_interfaces())
+            base_interface_layers.assign(intermediate_layers.size(), nullptr);
+        const auto smoothing_distance    = support_params.support_material_interface_flow.scaled_spacing() * 1.5;
+        const auto minimum_island_radius = support_params.support_material_interface_flow.scaled_spacing() / support_params.interface_density;
+        const auto closing_distance      = smoothing_distance; // scaled<float>(config.support_material_closing_radius.value);
+        // Insert a new layer into base_interface_layers, if intersection with base exists.
+        auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius](
+                SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, 
+                const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
+            bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty();
+            assert(! bottom.empty() || ! top.empty() || has_top_interface);
+            // Merge top into bottom, unite them with a safety offset.
+            append(bottom, std::move(top));
+            // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners).
+            bottom = intersection(
+                snug_supports ?
+                    smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
+                    union_safety_offset(std::move(bottom)),
+                intermediate_layer.polygons);
+            if (has_top_interface)
+                // Don't trim the precomputed Organic supports top interface with base layer
+                // as the precomputed top interface likely expands over multiple tree tips.
+                bottom = union_(std::move(top_interface_layer->polygons), bottom);
+            if (! bottom.empty()) {
+                //FIXME Remove non-printable tiny islands, let them be printed using the base support.
+                //bottom = opening(std::move(bottom), minimum_island_radius);
+                if (! bottom.empty()) {
+                    SupportGeneratorLayer &layer_new = top_interface_layer ? *top_interface_layer : layer_storage.allocate(type);
+                    layer_new.polygons   = std::move(bottom);
+                    layer_new.print_z    = intermediate_layer.print_z;
+                    layer_new.bottom_z   = intermediate_layer.bottom_z;
+                    layer_new.height     = intermediate_layer.height;
+                    layer_new.bridging   = intermediate_layer.bridging;
+                    // Subtract the interface from the base regions.
+                    intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons);
+                    if (subtract)
+                        // Trim the base interface layer with the interface layer.
+                        layer_new.polygons = diff(std::move(layer_new.polygons), *subtract);
+                    //FIXME filter layer_new.polygons islands by a minimum area?
+        //                  $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
+                    return &layer_new;
+                }
+            }
+            return nullptr;
+        };
+        tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
+            [&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params,
+             snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {                
+                // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
+                // this intermediate layer.
+                // Index of the first top contact layer intersecting the current intermediate layer.
+                auto idx_top_contact_first        = -1;
+                // Index of the first bottom contact layer intersecting the current intermediate layer.
+                auto idx_bottom_contact_first     = -1;
+                // Index of the first top interface layer intersecting the current intermediate layer.
+                auto idx_top_interface_first      = -1;
+                // Index of the first top contact interface layer intersecting the current intermediate layer.
+                auto idx_top_base_interface_first = -1;
+                auto num_intermediate = int(intermediate_layers.size());
+                for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) {
+                    SupportGeneratorLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer];
+                    Polygons polygons_top_contact_projected_interface;
+                    Polygons polygons_top_contact_projected_base;
+                    Polygons polygons_bottom_contact_projected_interface;
+                    Polygons polygons_bottom_contact_projected_base;
+                    if (support_params.num_top_interface_layers > 0) {
+                        // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
+                        coordf_t top_z              = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + int(support_params.num_top_interface_layers) - 1)]->print_z;
+                        coordf_t top_inteface_z     = std::numeric_limits<coordf_t>::max();
+                        if (support_params.num_top_base_interface_layers > 0)
+                            // Some top base interface layers will be generated.
+                            top_inteface_z = support_params.num_top_interface_layers_only() == 0 ?
+                                // Only base interface layers to generate.
+                                - std::numeric_limits<coordf_t>::max() :
+                                intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + int(support_params.num_top_interface_layers_only()) - 1)]->print_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 SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); //  - EPSILON
+                        // Collect the top contact areas above this intermediate layer, below top_z.
+                        for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) {
+                            const SupportGeneratorLayer &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(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, 
+                                // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
+                                // For grid supports, merging of support regions will be performed by the projection into grid.
+                                snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
+                        }
+                    }
+                    if (support_params.num_bottom_interface_layers > 0) {
+                        // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
+                        coordf_t bottom_z           = intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers) + 1)]->bottom_z;
+                        coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
+                        if (support_params.num_bottom_base_interface_layers > 0)
+                            // Some bottom base interface layers will be generated.
+                            bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ? 
+                                // Only base interface layers to generate.
+                                std::numeric_limits<coordf_t>::max() :
+                                intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z;
+                        // Move idx_bottom_contact_first up until touching bottom_z.
+                        idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
+                        // Collect the top contact areas above this intermediate layer, below top_z.
+                        for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) {
+                            const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact];
+                            if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z)
+                                break;
+                            polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons);
+                        }
+                    }
+                    auto resolve_same_layer = [](SupportGeneratorLayersPtr &layers, int &idx, coordf_t print_z) -> SupportGeneratorLayer* {
+                        if (! layers.empty()) {
+                            idx = idx_higher_or_equal(layers, idx, [print_z](const SupportGeneratorLayer *layer) { return layer->print_z > print_z - EPSILON; });
+                            if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON) {
+                                SupportGeneratorLayer *l = layers[idx];
+                                // Remove the layer from the source container, as it will be consumed here: It will be merged
+                                // with the newly produced interfaces.
+                                layers[idx] = nullptr;
+                                return l;
+                            }
+                        }
+                        return nullptr;
+                    };
+                    SupportGeneratorLayer *top_interface_layer      = resolve_same_layer(top_interface_layers, idx_top_interface_first, intermediate_layer.print_z);
+                    SupportGeneratorLayer *top_base_interface_layer = resolve_same_layer(top_base_interface_layers, idx_top_base_interface_first, intermediate_layer.print_z);
+                    SupportGeneratorLayer *interface_layer          = nullptr;
+                    if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty() ||
+                        (top_interface_layer && ! top_interface_layer->polygons.empty())) {
+                        interface_layer = insert_layer(
+                            intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), top_interface_layer,
+                            nullptr, polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface);
+                        interface_layers[idx_intermediate_layer] = interface_layer;
+                    }
+                    if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty() ||
+                        (top_base_interface_layer && ! top_base_interface_layer->polygons.empty()))
+                        base_interface_layers[idx_intermediate_layer] = insert_layer(
+                            intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), top_base_interface_layer,
+                            interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base);
+                }
+            });
+
+        // Compress contact_out, remove the nullptr items.
+        // The parallel_for above may not have merged all the interface and base_interface layers
+        // generated by the Organic supports code, do it here.
+        auto merge_remove_nulls = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) {
+            size_t nonzeros = std::count_if(in1.begin(), in1.end(), [](auto *l) { return l != nullptr; }) +
+                              std::count_if(in2.begin(), in2.end(), [](auto *l) { return l != nullptr; });
+            remove_nulls(in1);
+            remove_nulls(in2);
+            if (in2.empty())
+                return std::move(in1);
+            else if (in1.empty())
+                return std::move(in2);
+            else {
+                SupportGeneratorLayersPtr out(in1.size() + in2.size(), nullptr);
+                std::merge(in1.begin(), in1.end(), in2.begin(), in2.end(), out.begin(), [](auto* l, auto* r) { return l->print_z < r->print_z; });
+                return std::move(out);
+            }
+        };
+        interface_layers      = merge_remove_nulls(interface_layers,      top_interface_layers);
+        base_interface_layers = merge_remove_nulls(base_interface_layers, top_base_interface_layers);
+        BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
+    }
+    
+    return base_and_interface_layers;
+}
+
 SupportGeneratorLayersPtr generate_raft_base(
     const PrintObject                 &object,
     const SupportParameters           &support_params,
diff --git a/src/libslic3r/Support/SupportCommon.hpp b/src/libslic3r/Support/SupportCommon.hpp
index 4eabce772..19f5822fe 100644
--- a/src/libslic3r/Support/SupportCommon.hpp
+++ b/src/libslic3r/Support/SupportCommon.hpp
@@ -21,6 +21,23 @@ void remove_bridges_from_contacts(
     float                fw, 
     Polygons            &contact_polygons);
 
+// Turn some of the base layers into base interface layers.
+// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
+// extruder to improve adhesion of the soluble filament to the base.
+// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces
+// produced by this function.
+std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
+    const PrintObjectConfig           &config,
+    const SupportParameters           &support_params,
+    const SupportGeneratorLayersPtr   &bottom_contacts,
+    const SupportGeneratorLayersPtr   &top_contacts,
+    // Input / output, will be merged with output
+    SupportGeneratorLayersPtr         &top_interface_layers,
+    SupportGeneratorLayersPtr         &top_base_interface_layers,
+    // Input, will be trimmed with the newly created interface layers.
+    SupportGeneratorLayersPtr         &intermediate_layers,
+    SupportGeneratorLayerStorage      &layer_storage);
+
 // Generate raft layers, also expand the 1st support layer
 // in case there is no raft layer to improve support adhesion.
 SupportGeneratorLayersPtr generate_raft_base(
diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp
index 347278911..a21a48b9a 100644
--- a/src/libslic3r/Support/SupportMaterial.cpp
+++ b/src/libslic3r/Support/SupportMaterial.cpp
@@ -335,14 +335,16 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
 
     // Propagate top / bottom contact layers to generate interface layers 
     // and base interface layers (for soluble interface / non souble base only)
-    auto [interface_layers, base_interface_layers] = this->generate_interface_layers(bottom_contacts, top_contacts, intermediate_layers, layer_storage);
+    SupportGeneratorLayersPtr empty_layers;
+    auto [interface_layers, base_interface_layers] = FFFSupport::generate_interface_layers(
+        *m_object_config, m_support_params, bottom_contacts, top_contacts, empty_layers, empty_layers, intermediate_layers, layer_storage);
 
     BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft";
 
     // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
     // There is also a 1st intermediate layer containing bases of support columns.
     // Inflate the bases of the support columns and create the raft base under the object.
-    SupportGeneratorLayersPtr raft_layers = generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
+    SupportGeneratorLayersPtr raft_layers = FFFSupport::generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
 
 #ifdef SLIC3R_DEBUG
     for (const SupportGeneratorLayer *l : interface_layers)
@@ -2514,169 +2516,6 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
     BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
 }
 
-// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers.
-std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSupportMaterial::generate_interface_layers(
-    const SupportGeneratorLayersPtr   &bottom_contacts,
-    const SupportGeneratorLayersPtr   &top_contacts,
-    SupportGeneratorLayersPtr         &intermediate_layers,
-    SupportGeneratorLayerStorage      &layer_storage) const
-{
-//    my $area_threshold = $self->interface_flow->scaled_spacing ** 2;
-
-    std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> base_and_interface_layers;
-    SupportGeneratorLayersPtr &interface_layers       = base_and_interface_layers.first;
-    SupportGeneratorLayersPtr &base_interface_layers  = base_and_interface_layers.second;
-
-    // distinguish between interface and base interface layers
-    // Contact layer is considered an interface layer, therefore run the following block only if support_material_interface_layers > 1.
-    // Contact layer needs a base_interface layer, therefore run the following block if support_material_interface_layers > 0, has soluble support and extruders are different.
-    bool   soluble_interface_non_soluble_base =
-        // Zero z-gap between the overhangs and the support interface.
-        m_slicing_params.soluble_interface && 
-        // Interface extruder soluble.
-        m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) && 
-        // Base extruder: Either "print with active extruder" not soluble.
-        (m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1));
-    bool   snug_supports                 = m_object_config->support_material_style.value != smsGrid;
-    int num_interface_layers_top         = m_object_config->support_material_interface_layers;
-    int num_interface_layers_bottom      = m_object_config->support_material_bottom_interface_layers;
-    if (num_interface_layers_bottom < 0)
-        num_interface_layers_bottom = num_interface_layers_top;
-    int num_base_interface_layers_top    = soluble_interface_non_soluble_base ? std::min(num_interface_layers_top / 2, 2) : 0;
-    int num_base_interface_layers_bottom = soluble_interface_non_soluble_base ? std::min(num_interface_layers_bottom / 2, 2) : 0;
-
-    if (! intermediate_layers.empty() && (num_interface_layers_top > 1 || num_interface_layers_bottom > 1)) {
-        // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
-        BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
-        // Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1.
-        -- num_interface_layers_top;
-        -- num_interface_layers_bottom;
-        int num_interface_layers_only_top    = num_interface_layers_top    - num_base_interface_layers_top;
-        int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom;
-        interface_layers.assign(intermediate_layers.size(), nullptr);
-        if (num_base_interface_layers_top || num_base_interface_layers_bottom)
-            base_interface_layers.assign(intermediate_layers.size(), nullptr);
-        auto smoothing_distance              = m_support_params.support_material_interface_flow.scaled_spacing() * 1.5;
-        auto minimum_island_radius           = m_support_params.support_material_interface_flow.scaled_spacing() / m_support_params.interface_density;
-        auto closing_distance                = smoothing_distance; // scaled<float>(m_object_config->support_material_closing_radius.value);
-        // Insert a new layer into base_interface_layers, if intersection with base exists.
-        auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius](
-                SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
-            assert(! bottom.empty() || ! top.empty());
-            // Merge top into bottom, unite them with a safety offset.
-            append(bottom, std::move(top));
-            // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners).
-            bottom = intersection(
-                snug_supports ?
-                    smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
-                    union_safety_offset(std::move(bottom)),
-                intermediate_layer.polygons);
-            if (! bottom.empty()) {
-                //FIXME Remove non-printable tiny islands, let them be printed using the base support.
-                //bottom = opening(std::move(bottom), minimum_island_radius);
-                if (! bottom.empty()) {
-                    SupportGeneratorLayer &layer_new = layer_storage.allocate(type);
-                    layer_new.polygons   = std::move(bottom);
-                    layer_new.print_z    = intermediate_layer.print_z;
-                    layer_new.bottom_z   = intermediate_layer.bottom_z;
-                    layer_new.height     = intermediate_layer.height;
-                    layer_new.bridging   = intermediate_layer.bridging;
-                    // Subtract the interface from the base regions.
-                    intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons);
-                    if (subtract)
-                        // Trim the base interface layer with the interface layer.
-                        layer_new.polygons = diff(std::move(layer_new.polygons), *subtract);
-                    //FIXME filter layer_new.polygons islands by a minimum area?
-        //                  $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
-                    return &layer_new;
-                }
-            }
-            return nullptr;
-        };
-        tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
-            [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, 
-             num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom,
-             snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {                
-                // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
-                // this intermediate layer.
-                // Index of the first top contact layer intersecting the current intermediate layer.
-                auto idx_top_contact_first      = -1;
-                // Index of the first bottom contact layer intersecting the current intermediate layer.
-                auto idx_bottom_contact_first   = -1;
-                auto num_intermediate = int(intermediate_layers.size());
-                for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) {
-                    SupportGeneratorLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer];
-                    Polygons polygons_top_contact_projected_interface;
-                    Polygons polygons_top_contact_projected_base;
-                    Polygons polygons_bottom_contact_projected_interface;
-                    Polygons polygons_bottom_contact_projected_base;
-                    if (num_interface_layers_top > 0) {
-                        // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
-                        coordf_t top_z              = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z;
-                        coordf_t top_inteface_z     = std::numeric_limits<coordf_t>::max();
-                        if (num_base_interface_layers_top > 0)
-                            // Some top base interface layers will be generated.
-                            top_inteface_z = num_interface_layers_only_top == 0 ?
-                                // Only base interface layers to generate.
-                                - std::numeric_limits<coordf_t>::max() :
-                                intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_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 SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); //  - EPSILON
-                        // Collect the top contact areas above this intermediate layer, below top_z.
-                        for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) {
-                            const SupportGeneratorLayer &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(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, 
-                                // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
-                                // For grid supports, merging of support regions will be performed by the projection into grid.
-                                snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
-                        }
-                    }
-                    if (num_interface_layers_bottom > 0) {
-                        // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
-                        coordf_t bottom_z           = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z;
-                        coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
-                        if (num_base_interface_layers_bottom > 0)
-                            // Some bottom base interface layers will be generated.
-                            bottom_interface_z = num_interface_layers_only_bottom == 0 ? 
-                                // Only base interface layers to generate.
-                                std::numeric_limits<coordf_t>::max() :
-                                intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z;
-                        // Move idx_bottom_contact_first up until touching bottom_z.
-                        idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
-                        // Collect the top contact areas above this intermediate layer, below top_z.
-                        for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) {
-                            const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact];
-                            if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z)
-                                break;
-                            polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons);
-                        }
-                    }
-                    SupportGeneratorLayer *interface_layer = nullptr;
-                    if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) {
-                        interface_layer = insert_layer(
-                            intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), nullptr,
-                            polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface);
-                        interface_layers[idx_intermediate_layer] = interface_layer;
-                    }
-                    if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty())
-                        base_interface_layers[idx_intermediate_layer] = insert_layer(
-                            intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), 
-                            interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base);
-                }
-            });
-
-        // Compress contact_out, remove the nullptr items.
-        remove_nulls(interface_layers);
-        remove_nulls(base_interface_layers);
-        BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
-    }
-    
-    return base_and_interface_layers;
-}
-
 /*
 void PrintObjectSupportMaterial::clip_by_pillars(
     const PrintObject   &object,
diff --git a/src/libslic3r/Support/SupportMaterial.hpp b/src/libslic3r/Support/SupportMaterial.hpp
index 924ed1a05..4f1768fb1 100644
--- a/src/libslic3r/Support/SupportMaterial.hpp
+++ b/src/libslic3r/Support/SupportMaterial.hpp
@@ -72,16 +72,6 @@ private:
 	    SupportGeneratorLayersPtr         &intermediate_layers,
 	    const std::vector<Polygons> &layer_support_areas) const;
 
-	// Turn some of the base layers into base interface layers.
-	// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
-	// extruder to improve adhesion of the soluble filament to the base.
-	std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
-	    const SupportGeneratorLayersPtr   &bottom_contacts,
-	    const SupportGeneratorLayersPtr   &top_contacts,
-	    SupportGeneratorLayersPtr         &intermediate_layers,
-	    SupportGeneratorLayerStorage      &layer_storage) const;
-	
-
 	// Trim support layers by an object to leave a defined gap between
 	// the support volume and the object.
 	void trim_support_layers_by_object(
diff --git a/src/libslic3r/Support/SupportParameters.cpp b/src/libslic3r/Support/SupportParameters.cpp
index 1c7f860b8..531e8dcaf 100644
--- a/src/libslic3r/Support/SupportParameters.cpp
+++ b/src/libslic3r/Support/SupportParameters.cpp
@@ -11,6 +11,33 @@ SupportParameters::SupportParameters(const PrintObject &object)
     const PrintObjectConfig &object_config  = object.config();
     const SlicingParameters &slicing_params = object.slicing_parameters();
 
+    this->soluble_interface = slicing_params.soluble_interface;
+    this->soluble_interface_non_soluble_base =
+        // Zero z-gap between the overhangs and the support interface.
+        slicing_params.soluble_interface &&
+        // Interface extruder soluble.
+        object_config.support_material_interface_extruder.value > 0 && print_config.filament_soluble.get_at(object_config.support_material_interface_extruder.value - 1) &&
+        // Base extruder: Either "print with active extruder" not soluble.
+        (object_config.support_material_extruder.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_material_extruder.value - 1));
+
+    {
+        int num_top_interface_layers    = std::max(0, object_config.support_material_interface_layers.value);
+        int num_bottom_interface_layers = object_config.support_material_bottom_interface_layers < 0 ? 
+            num_top_interface_layers : object_config.support_material_bottom_interface_layers;
+        this->has_top_contacts              = num_top_interface_layers    > 0;
+        this->has_bottom_contacts           = num_bottom_interface_layers > 0;
+        this->num_top_interface_layers      = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
+        this->num_bottom_interface_layers   = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
+        if (this->soluble_interface_non_soluble_base) {
+            // Try to support soluble dense interfaces with non-soluble dense interfaces.
+            this->num_top_base_interface_layers    = size_t(std::min(num_top_interface_layers / 2, 2));
+            this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
+        } else {
+            this->num_top_base_interface_layers    = 0;
+            this->num_bottom_base_interface_layers = 0;
+        }
+    }
+
     this->first_layer_flow                   = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
     this->support_material_flow              = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
     this->support_material_interface_flow    = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
@@ -54,7 +81,6 @@ SupportParameters::SupportParameters(const PrintObject &object)
             this->can_merge_support_regions = true;
     }
 
-
     double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing();
     this->interface_density  = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
     double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing();
diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp
index 904e8ffe2..be38e9650 100644
--- a/src/libslic3r/Support/SupportParameters.hpp
+++ b/src/libslic3r/Support/SupportParameters.hpp
@@ -14,6 +14,30 @@ namespace FFFSupport {
 struct SupportParameters {
 	SupportParameters(const PrintObject &object);
 
+    // Both top / bottom contacts and interfaces are soluble.
+    bool                    soluble_interface;
+    // Support contact & interface are soluble, but support base is non-soluble.
+    bool                    soluble_interface_non_soluble_base;
+
+    // Is there at least a top contact layer extruded above support base?
+    bool                    has_top_contacts;
+    // Is there at least a bottom contact layer extruded below support base?
+    bool                    has_bottom_contacts;
+    // Number of top interface layers without counting the contact layer.
+    size_t                  num_top_interface_layers;
+    // Number of bottom interface layers without counting the contact layer.
+    size_t                  num_bottom_interface_layers;
+    // Number of top base interface layers. Zero if not soluble_interface_non_soluble_base.
+    size_t                  num_top_base_interface_layers;
+    // Number of bottom base interface layers. Zero if not soluble_interface_non_soluble_base.
+    size_t                  num_bottom_base_interface_layers;
+
+    bool                    has_contacts() const { return this->has_top_contacts || this->has_bottom_contacts; }
+    bool                    has_interfaces() const { return this->num_top_interface_layers + this->num_bottom_interface_layers > 0; }
+    bool                    has_base_interfaces() const { return this->num_top_base_interface_layers + this->num_bottom_base_interface_layers > 0; }
+    size_t                  num_top_interface_layers_only() const { return this->num_top_interface_layers - this->num_top_base_interface_layers; }
+    size_t                  num_bottom_interface_layers_only() const { return this->num_bottom_interface_layers - this->num_bottom_base_interface_layers; }
+
 	// Flow at the 1st print layer.
 	Flow 					first_layer_flow;
 	// Flow at the support base (neither top, nor bottom interface).
diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp
index d1306962e..dfeb3fdf9 100644
--- a/src/libslic3r/Support/TreeSupport.cpp
+++ b/src/libslic3r/Support/TreeSupport.cpp
@@ -87,7 +87,6 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
       bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
       z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
       z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
-      performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)),
 //              support_infill_angles(mesh_group_settings.support_infill_angles),
       support_roof_angles(mesh_group_settings.support_roof_angles),
       roof_pattern(mesh_group_settings.support_roof_pattern),
@@ -1077,71 +1076,53 @@ void finalize_raft_contact(
     }
 }
 
+// Used by generate_initial_areas() in parallel by multiple layers.
 class InterfacePlacer {
 public:
-    InterfacePlacer(const SlicingParameters &slicing_parameters, const TreeModelVolumes &volumes, const TreeSupportSettings &config, bool force_tip_to_roof, size_t num_support_layers,
-        std::vector<SupportElements> &move_bounds, SupportGeneratorLayerStorage &layer_storage, SupportGeneratorLayersPtr &top_contacts) :
-        slicing_parameters(slicing_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof),
-        move_bounds(move_bounds), layer_storage(layer_storage), top_contacts(top_contacts) {
+    InterfacePlacer(
+        const SlicingParameters &slicing_parameters, 
+        const SupportParameters &support_parameters,
+        const TreeModelVolumes &volumes, const TreeSupportSettings &config, 
+        bool force_tip_to_roof, size_t num_support_layers,
+        std::vector<SupportElements> &move_bounds, 
+        SupportGeneratorLayerStorage &layer_storage, 
+        SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interfaces, SupportGeneratorLayersPtr &top_base_interfaces) 
+    :
+        slicing_parameters(slicing_parameters), support_parameters(support_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof),
+        move_bounds(move_bounds), 
+        layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces)  {
         m_already_inserted.assign(num_support_layers, {});
         this->min_xy_dist = config.xy_distance > config.xy_min_distance;
     }
     const SlicingParameters                            &slicing_parameters;
+    const SupportParameters                            &support_parameters;
     const TreeModelVolumes                             &volumes;
     const TreeSupportSettings                          &config;
-    bool                                                force_tip_to_roof;
+    // Radius of the tree tip is large enough to be covered by an interface.
+    const bool                                          force_tip_to_roof;
     bool                                                min_xy_dist;
     
-    // Outputs
-    std::vector<SupportElements>                       &move_bounds;
-    SupportGeneratorLayerStorage                       &layer_storage;
-    SupportGeneratorLayersPtr                          &top_contacts;
-
-private:
-    // Temps
-    static constexpr const auto                         m_base_radius = scaled<int>(0.01);
-    const Polygon                                       m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
-    
-    // Mutexes, guards
-    std::mutex                                          m_mutex_movebounds;
-    std::mutex                                          m_mutex_layer_storage;
-    std::vector<std::unordered_set<Point, PointHash>>   m_already_inserted;
-
 public:
-    void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx)
-    {
-        SupportGeneratorLayer*& l = top_contacts[insert_layer_idx];
-        if (l == nullptr)
-            l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, insert_layer_idx);
-        // will be unioned in finalize_interface_and_support_areas()
-        append(l->polygons, std::move(new_roofs));
-    }
-
-    void add_roof(Polygons &&new_roofs, const size_t insert_layer_idx)
-    {
-        std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-        add_roof_unguarded(std::move(new_roofs), insert_layer_idx);
-    }
-
-    void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
+    // called by sample_overhang_area()
+    // Insert the contact layer and some of the inteface and base interface layers below.
+    void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx)
     {
         if (! new_roofs.empty()) {
             std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-            for (size_t idx = 0; idx < dtt_roof; ++ idx)
+            for (size_t idx = 0; idx < new_roofs.size(); ++ idx)
                 if (! new_roofs[idx].empty())
-                    add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx);
+                    add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx);
         }
     }
 
-    void add_roof_build_plate(Polygons &&overhang_areas)
+    // called by sample_overhang_area()
+    void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
     {
         std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-        SupportGeneratorLayer*& l = top_contacts[0];
-        if (l == nullptr)
-            l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, 0);
-        append(l->polygons, std::move(overhang_areas));
+        this->add_roof_unguarded(std::move(overhang_areas), 0, dtt_roof);
     }
 
+    // called by sample_overhang_area()
     void add_points_along_lines(
         // Insert points (tree tips or top contact interfaces) along these lines.
         LineInformations    lines, 
@@ -1150,7 +1131,7 @@ public:
         // Insert this number of interface layers.
         size_t              roof_tip_layers,
         // True if an interface is already generated above these lines.
-        bool                supports_roof,
+        size_t              supports_roof_layers,
         // The element tries to not move until this dtt is reached.
         size_t              dont_move_until)
     {
@@ -1203,7 +1184,7 @@ public:
                     roof_circle.translate(p.first);
                     new_roofs.emplace_back(std::move(roof_circle));
                 }
-            this->add_roof(std::move(new_roofs), this_layer_idx);
+            this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
         }
 
         for (const LineInformation &line : lines) {
@@ -1215,11 +1196,33 @@ public:
                     // don't move until
                     dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
                     // supports roof
-                    dtt_roof_tip > 0 || supports_roof,
+                    dtt_roof_tip + supports_roof_layers > 0,
                     disable_ovalistation);
         }
     }
 
+private:
+    void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
+    {
+        SupportGeneratorLayersPtr &layers =
+            dtt_roof == 0 ? this->top_contacts :
+            dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces;
+        SupportGeneratorLayer*& l = layers[insert_layer_idx];
+        if (l == nullptr)
+            l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface, 
+                    slicing_parameters, config, insert_layer_idx);
+        // will be unioned in finalize_interface_and_support_areas()
+        append(l->polygons, std::move(new_roofs));
+    }
+
+    // called by this->add_points_along_lines()
+    void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip)
+    {
+        std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
+        add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip);
+    }
+
+    // called by this->add_points_along_lines()
     void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation)
     {
         bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
@@ -1233,8 +1236,8 @@ public:
         Polygons circle{ m_base_circle };
         circle.front().translate(p.first);
         {
-            std::lock_guard<std::mutex> critical_section_movebounds(m_mutex_movebounds);
             Point hash_pos = p.first / ((config.min_radius + 1) / 10);
+            std::lock_guard<std::mutex> critical_section_movebounds(m_mutex_movebounds);
             if (!m_already_inserted[insert_layer].count(hash_pos)) {
                 // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
                 m_already_inserted[insert_layer].emplace(hash_pos);
@@ -1261,9 +1264,26 @@ public:
                 move_bounds[insert_layer].emplace_back(state, std::move(circle));
             }
         }
-    };
+    }
+
+    // Outputs
+    std::vector<SupportElements>                       &move_bounds;
+    SupportGeneratorLayerStorage                       &layer_storage;
+    SupportGeneratorLayersPtr                          &top_contacts;
+    SupportGeneratorLayersPtr                          &top_interfaces;
+    SupportGeneratorLayersPtr                          &top_base_interfaces;
+
+    // Temps
+    static constexpr const auto                         m_base_radius = scaled<int>(0.01);
+    const Polygon                                       m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
+    
+    // Mutexes, guards
+    std::mutex                                          m_mutex_movebounds;
+    std::mutex                                          m_mutex_layer_storage;
+    std::vector<std::unordered_set<Point, PointHash>>   m_already_inserted;
 };
 
+// Called by generate_initial_areas(), used in parallel by multiple layers.
 // Produce
 // 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
 // 2) Tree tips supporting either the roof layers or the object itself.
@@ -1280,13 +1300,12 @@ void sample_overhang_area(
     const bool                           large_horizontal_roof,
     // Index of the top suport layer generated by this function.
     const size_t                         layer_idx,
-    // Number of roof (contact, interface) layers between the overhang and tree tips.
+    // Maximum number of roof (contact, interface) layers between the overhang and tree tips to be generated.
     const size_t                         num_support_roof_layers,
     // 
     const coord_t                        connect_length,
     // Configuration classes
     const TreeSupportMeshGroupSettings  &mesh_group_settings,
-    const SupportParameters             &support_params,
     // Configuration & Output
     InterfacePlacer                     &interface_placer)
 {
@@ -1297,11 +1316,12 @@ void sample_overhang_area(
     // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from 
     // a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior
     // for each pattern harms maintainability as it very well could be >100 LOC
-    auto generate_roof_lines = [&support_params, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines {
-        return generate_support_infill_lines(area, support_params, true, layer_idx, mesh_group_settings.support_roof_line_distance);
+    auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines {
+        return generate_support_infill_lines(area, interface_placer.support_parameters, true, layer_idx, mesh_group_settings.support_roof_line_distance);
     };
 
     LineInformations        overhang_lines;
+    // Track how many top contact / interface layers were already generated.
     size_t                  dtt_roof             = 0;
     size_t                  layer_generation_dtt = 0;
 
@@ -1325,9 +1345,9 @@ void sample_overhang_area(
             }
             Polygons overhang_area_next = diff(overhang_area, forbidden_next);
             if (area(overhang_area_next) < mesh_group_settings.minimum_roof_area) {
-                // next layer down the roof area would be to small so we have to insert our roof support here. Also convert squaremicrons to squaremilimeter
-                if (dtt_roof != 0) {
-                    size_t dtt_before = dtt_roof > 0 ? dtt_roof - 1 : 0;
+                // Next layer down the roof area would be to small so we have to insert our roof support here.
+                if (dtt_roof > 0) {
+                    size_t dtt_before = dtt_roof - 1;
                     // Produce support head points supporting an interface layer: First produce the interface lines, then sample them.
                     overhang_lines = split_lines(
                         convert_lines_to_internal(interface_placer.volumes, interface_placer.config,
@@ -1354,7 +1374,8 @@ void sample_overhang_area(
                     break;
                 }
             }
-        interface_placer.add_roofs(std::move(added_roofs), layer_idx, dtt_roof);
+        added_roofs.erase(added_roofs.begin() + dtt_roof, added_roofs.end());
+        interface_placer.add_roofs(std::move(added_roofs), layer_idx);
     }
 
     if (overhang_lines.empty()) {
@@ -1364,7 +1385,7 @@ void sample_overhang_area(
         bool supports_roof   = dtt_roof > 0;
         bool continuous_tips = ! supports_roof && large_horizontal_roof;
         Polylines polylines = ensure_maximum_distance_polyline(
-            generate_support_infill_lines(overhang_area, support_params, supports_roof, layer_idx - layer_generation_dtt,
+            generate_support_infill_lines(overhang_area, interface_placer.support_parameters, supports_roof, layer_idx - layer_generation_dtt,
                 supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance),
             continuous_tips ? interface_placer.config.min_radius / 2 : connect_length, 1);
         size_t point_count = 0;
@@ -1388,9 +1409,10 @@ void sample_overhang_area(
         overhang_lines = convert_lines_to_internal(interface_placer.volumes, interface_placer.config, polylines, layer_idx - dtt_roof);
     }
 
+    assert(dtt_roof <= layer_idx);
     if (int(dtt_roof) >= layer_idx && large_horizontal_roof)
-        // reached buildplate
-        interface_placer.add_roof_build_plate(std::move(overhang_area));
+        // Reached buildplate when generating contact, interface and base interface layers.
+        interface_placer.add_roof_build_plate(std::move(overhang_area), dtt_roof);
     else {
         // normal trees have to be generated
         const bool roof_enabled = num_support_roof_layers > 0;
@@ -1401,8 +1423,8 @@ void sample_overhang_area(
             layer_idx - dtt_roof,
             // Remaining roof tip layers.
             interface_placer.force_tip_to_roof ? num_support_roof_layers - dtt_roof : 0,
-            // Supports roof already?
-            dtt_roof > 0,
+            // Supports roof already? How many roof layers were already produced above these tips?
+            dtt_roof,
             // Don't move until the following distance to top is reached.
             roof_enabled ? num_support_roof_layers - dtt_roof : 0);
     }
@@ -1424,7 +1446,8 @@ static void generate_initial_areas(
     const std::vector<Polygons>     &overhangs,
     std::vector<SupportElements>    &move_bounds,
     SupportGeneratorLayersPtr       &top_contacts,
-    SupportGeneratorLayersPtr       &top_interface_layers,
+    SupportGeneratorLayersPtr       &top_interfaces,
+    SupportGeneratorLayersPtr       &top_base_interfaces,
     SupportGeneratorLayerStorage    &layer_storage,
     std::function<void()>            throw_on_cancel)
 {
@@ -1462,7 +1485,7 @@ static void generate_initial_areas(
         ;
     const size_t  num_support_roof_layers = mesh_group_settings.support_roof_layers;
     const bool    roof_enabled        = num_support_roof_layers > 0;
-    const bool    force_tip_to_roof   = sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled;
+    const bool    force_tip_to_roof   = roof_enabled && sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area;
     // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point 
     // may cause extra material and time cost.  Could also be an user setting or differently calculated. Idea is that if an overhang 
     // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. 
@@ -1496,9 +1519,9 @@ static void generate_initial_areas(
                 raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
     }
 
-    InterfacePlacer interface_placer{ print_object.slicing_parameters(), volumes, config, force_tip_to_roof, num_support_layers, 
+    InterfacePlacer interface_placer{ print_object.slicing_parameters(), support_params, volumes, config, force_tip_to_roof, num_support_layers,
         // Outputs
-        move_bounds, layer_storage, top_contacts };
+        move_bounds, layer_storage, top_contacts, top_interfaces, top_base_interfaces };
 
     tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
         [&volumes, &config, &raw_overhangs, &mesh_group_settings, &support_params,
@@ -1613,7 +1636,7 @@ static void generate_initial_areas(
                 //check_self_intersections(overhang_regular, "overhang_regular3");
                 for (ExPolygon &roof_part : union_ex(overhang_roofs)) {
                     sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length,
-                        mesh_group_settings, support_params, interface_placer);
+                        mesh_group_settings, interface_placer);
                     throw_on_cancel();
                 }
             }
@@ -1623,9 +1646,8 @@ static void generate_initial_areas(
                 remove_small(overhang_regular, mesh_group_settings.minimum_support_area);
             for (ExPolygon &support_part : union_ex(overhang_regular)) {
                 sample_overhang_area(to_polygons(std::move(support_part)), 
-                    // Don't 
                     false, layer_idx, num_support_roof_layers, connect_length,
-                    mesh_group_settings, support_params, interface_placer);
+                    mesh_group_settings, interface_placer);
                 throw_on_cancel();
             }
         }
@@ -3392,7 +3414,7 @@ static void finalize_interface_and_support_areas(
                     //FIXME subtract the wipe tower 
                     append(floor_layer, intersection(layer_outset, overhangs[sample_layer]));
                     if (layers_below < config.support_bottom_layers)
-                        layers_below = std::min(layers_below + config.performance_interface_skip_layers, config.support_bottom_layers);
+                        layers_below = std::min(layers_below + 1, config.support_bottom_layers);
                     else
                         break;
                 }
@@ -4139,11 +4161,20 @@ static void organic_smooth_branches_avoid_collisions(
 #endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW
 
 // Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
-static std::vector<Polygons> draw_branches(
+static void draw_branches(
     PrintObject                     &print_object,
     TreeModelVolumes                &volumes, 
     const TreeSupportSettings       &config,
     std::vector<SupportElements>    &move_bounds,
+
+    // I/O:
+    SupportGeneratorLayersPtr       &bottom_contacts,
+    SupportGeneratorLayersPtr       &top_contacts,
+
+    // Output:
+    SupportGeneratorLayersPtr       &intermediate_layers,
+    SupportGeneratorLayerStorage    &layer_storage,
+
     std::function<void()>            throw_on_cancel)
 {
     // All SupportElements are put into a layer independent storage to improve parallelization.
@@ -4211,7 +4242,7 @@ static std::vector<Polygons> draw_branches(
 
     struct Slice {
         Polygons polygons;
-        Polygons bottom_interfaces;
+        Polygons bottom_contacts;
         size_t   num_branches{ 0 };
     };
 
@@ -4316,6 +4347,7 @@ static std::vector<Polygons> draw_branches(
         [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
             indexed_triangle_set    partial_mesh;
             std::vector<float>      slice_z;
+            std::vector<Polygons>   bottom_contacts;
             for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
                 Tree &tree = trees[tree_id];
                 for (const Branch &branch : tree.branches) {
@@ -4335,63 +4367,71 @@ static std::vector<Polygons> draw_branches(
                         slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
                     }
                     std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
+                    bottom_contacts.clear();
                     //FIXME parallelize?
                     for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i)
                         slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
 
                     size_t num_empty = 0;
-                    if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) {
-                        // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
-                        struct BottomExtraSlice {
-                            Polygons polygons;
-                            Polygons supported;
-                            Polygons bottom_interfaces;
-                            double   area;
-                            double   supported_area;
-                        };
-                        std::vector<BottomExtraSlice>   bottom_extra_slices;
-                        Polygons                        rest_support;
-                        coord_t                         bottom_radius = config.getRadius(branch.path.front()->state);
-                        // Don't propagate further than 1.5 * bottom radius.
-                        //LayerIndex                      layers_propagate_max = 2 * bottom_radius / config.layer_height;
-                        LayerIndex                      layers_propagate_max = 5 * bottom_radius / config.layer_height;
-                        LayerIndex                      layer_bottommost = std::max(0, layer_begin - layers_propagate_max);
-                        // Only propagate until the rest area is smaller than this threshold.
-                        double                          support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius));
-                        // Only propagate until the rest area is smaller than this threshold.
-                        double                          support_area_min = 0.1 * M_PI * sqr(double(config.min_radius));
-                        for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
-                            rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false));
-                            double rest_support_area = area(rest_support);
-                            if (rest_support_area < support_area_stop)
-                                // Don't propagate a fraction of the tree contact surface.
-                                break;
-                            // Measure how much the rest_support is actually supported.
-                            /*
-                            Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{}));
-                            double supported_area = area(supported);
-                            printf("Supported area: %d, %lf\n", layer_idx, supported_area);
-                            */
-                            Polygons supported;
-                            double supported_area;
-                            bottom_extra_slices.push_back({ rest_support, std::move(supported), {}, rest_support_area, supported_area });
-                        }
-                        // Now remove those bottom slices that are not supported at all.
-                        while (! bottom_extra_slices.empty()) {
-                            Polygons bottom_interfaces = intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
-                            if (area(bottom_interfaces) < support_area_min)
-                                bottom_extra_slices.pop_back();
-                            else {
-                                bottom_extra_slices.back().bottom_interfaces = std::move(bottom_interfaces);
+                    if (slices.front().empty()) {
+                        // Some of the initial layers are empty.
+                        num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
+                    } else {
+                        if (branch.has_root) {
+                            if (branch.path.front()->state.to_model_gracious) {
+                                if (config.settings.support_floor_layers > 0)
+                                    //FIXME one may just take the whole tree slice as bottom interface.
+                                    bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {})));
+                            } else if (layer_begin > 0) {
+                                // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
+                                struct BottomExtraSlice {
+                                    Polygons polygons;
+                                    double   area;
+                                };
+                                std::vector<BottomExtraSlice>   bottom_extra_slices;
+                                Polygons                        rest_support;
+                                coord_t                         bottom_radius = config.getRadius(branch.path.front()->state);
+                                // Don't propagate further than 1.5 * bottom radius.
+                                //LayerIndex                      layers_propagate_max = 2 * bottom_radius / config.layer_height;
+                                LayerIndex                      layers_propagate_max = 5 * bottom_radius / config.layer_height;
+                                LayerIndex                      layer_bottommost = std::max(0, layer_begin - layers_propagate_max);
+                                // Only propagate until the rest area is smaller than this threshold.
+                                double                          support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius));
+                                // Only propagate until the rest area is smaller than this threshold.
+                                double                          support_area_min = 0.1 * M_PI * sqr(double(config.min_radius));
+                                for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
+                                    rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false));
+                                    double rest_support_area = area(rest_support);
+                                    if (rest_support_area < support_area_stop)
+                                        // Don't propagate a fraction of the tree contact surface.
+                                        break;
+                                    bottom_extra_slices.push_back({ rest_support, rest_support_area });
+                                }
+                                // Now remove those bottom slices that are not supported at all.
+                                while (! bottom_extra_slices.empty()) {
+                                    Polygons this_bottom_contacts = intersection_clipped(
+                                        bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
+                                    if (area(this_bottom_contacts) < support_area_min)
+                                        bottom_extra_slices.pop_back();
+                                    else if (config.settings.support_floor_layers > 0)
+                                        bottom_contacts.emplace_back(std::move(this_bottom_contacts));
+                                }
+                                if (config.settings.support_floor_layers > 0)
+                                    for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i)
+                                        bottom_contacts.emplace_back(
+                                            intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {})));
+                                layer_begin -= LayerIndex(bottom_extra_slices.size());
+                                slices.insert(slices.begin(), bottom_extra_slices.size(), {});
+                                auto it_dst = slices.begin();
+                                for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src)
+                                    *it_dst ++ = std::move(it_src->polygons);
                             }
                         }
-                        layer_begin -= LayerIndex(bottom_extra_slices.size());
-                        slices.insert(slices.begin(), bottom_extra_slices.size(), {});
-                        size_t i = 0;
-                        for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i)
-                            slices[i] = std::move(it->polygons);
-                    } else 
-                        num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
+                        if (branch.has_tip) {
+                            // Add top slices to top contacts / interfaces / base interfaces.
+                            //slices;
+                        }
+                    }
 
                     layer_begin += LayerIndex(num_empty);
                     while (! slices.empty() && slices.back().empty()) {
@@ -4414,14 +4454,21 @@ static std::vector<Polygons> draw_branches(
                             tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
                         tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
                         layer_begin -= LayerIndex(num_empty);
-                        for (LayerIndex i = layer_begin; i != layer_end; ++ i)
-                            if (Polygons &src = slices[i - layer_begin]; ! src.empty()) {
+                        for (LayerIndex i = layer_begin; i != layer_end; ++ i) {
+                            int j = i - layer_begin;
+                            if (Polygons &src = slices[j]; ! src.empty()) {
                                 Slice &dst = tree.slices[i - new_begin];
-                                if (++ dst.num_branches > 1)
+                                if (++ dst.num_branches > 1) {
                                     append(dst.polygons, std::move(src));
-                                else
+                                    if (j < bottom_contacts.size())
+                                        append(dst.bottom_contacts, std::move(bottom_contacts[j]));
+                                } else {
                                     dst.polygons = std::move(std::move(src));
+                                    if (j < bottom_contacts.size())
+                                        dst.bottom_contacts = std::move(bottom_contacts[j]);
+                                }
                             }
+                        }
                         tree.first_layer_id = new_begin;
                     }
                 }
@@ -4434,7 +4481,8 @@ static std::vector<Polygons> draw_branches(
             Tree &tree = trees[tree_id];
             for (Slice &slice : tree.slices)
                 if (slice.num_branches > 1) {
-                    slice.polygons = union_(slice.polygons);
+                    slice.polygons        = union_(slice.polygons);
+                    slice.bottom_contacts = union_(slice.bottom_contacts);
                     slice.num_branches = 1;
                 }
             throw_on_cancel();
@@ -4452,25 +4500,55 @@ static std::vector<Polygons> draw_branches(
             for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i)
                 if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) {
                     Slice &dst = slices[i];
-                    if (++ dst.num_branches > 1)
-                        append(dst.polygons, std::move(src.polygons));
-                    else
-                        dst.polygons = std::move(src.polygons);
+                    if (++ dst.num_branches > 1) {
+                        append(dst.polygons,        std::move(src.polygons));
+                        append(dst.bottom_contacts, std::move(src.bottom_contacts));
+                    } else {
+                        dst.polygons        = std::move(src.polygons);
+                        dst.bottom_contacts = std::move(src.bottom_contacts);
+                    }
                 }
         }
 
-    std::vector<Polygons> support_layer_storage(move_bounds.size());
     tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
-        [&slices, &support_layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
-        for (size_t slice_id = range.begin(); slice_id < range.end(); ++ slice_id) {
-            Slice &slice = slices[slice_id];
-            support_layer_storage[slice_id] = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
+        [&print_object, &config, &slices, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
+        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
+            Slice &slice = slices[layer_idx];
+            assert(intermediate_layers[layer_idx] == nullptr);
+            Polygons base_layer_polygons     = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
+            Polygons bottom_contact_polygons = slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts);
+
+            if (! base_layer_polygons.empty()) {
+                // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
+                base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
+                //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
+                // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
+                base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)), polygons_strictly_simple);
+            }
+
+            // Subtract top contact layer polygons from support base.
+            SupportGeneratorLayer *top_contact_layer = top_contacts[layer_idx];
+            if (top_contact_layer && ! top_contact_layer->polygons.empty() && ! base_layer_polygons.empty()) {
+                base_layer_polygons = diff(base_layer_polygons, top_contact_layer->polygons);
+                if (! bottom_contact_polygons.empty())
+                    //FIXME it may be better to clip bottom contacts with top contacts first after they are propagated to produce interface layers.
+                    bottom_contact_polygons = diff(bottom_contact_polygons, top_contact_layer->polygons);
+            }
+            if (! bottom_contact_polygons.empty()) {
+                base_layer_polygons = diff(base_layer_polygons, bottom_contact_polygons);
+                SupportGeneratorLayer *bottom_contact_layer = bottom_contacts[layer_idx] = &layer_allocate(
+                    layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
+                bottom_contact_layer->polygons = std::move(bottom_contact_polygons);
+            }
+            if (! base_layer_polygons.empty()) {
+                SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(
+                    layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
+                base_layer->polygons = union_(base_layer_polygons);
+            }
+
             throw_on_cancel();
         }
     }, tbb::simple_partitioner());
-
-    //FIXME simplify!
-    return support_layer_storage;
 }
 
 /*!
@@ -4538,9 +4616,12 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
         SupportGeneratorLayerStorage layer_storage;
         SupportGeneratorLayersPtr    top_contacts;
         SupportGeneratorLayersPtr    bottom_contacts;
-        SupportGeneratorLayersPtr    top_interface_layers;
+        SupportGeneratorLayersPtr    interface_layers;
+        SupportGeneratorLayersPtr    base_interface_layers;
         SupportGeneratorLayersPtr    intermediate_layers;
 
+        SupportParameters support_params(print_object);
+
         if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
             num_support_layers > 0) {
 
@@ -4550,13 +4631,15 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
             std::vector<SupportElements> move_bounds(num_support_layers);
 
             // ### Place tips of the support tree
-            top_contacts        .assign(num_support_layers, nullptr);
-            bottom_contacts     .assign(num_support_layers, nullptr);
-            top_interface_layers.assign(num_support_layers, nullptr);
-            intermediate_layers .assign(num_support_layers, nullptr);
+            bottom_contacts      .assign(num_support_layers, nullptr);
+            top_contacts         .assign(num_support_layers, nullptr);
+            interface_layers     .assign(num_support_layers, nullptr);
+            base_interface_layers.assign(num_support_layers, nullptr);
+            intermediate_layers  .assign(num_support_layers, nullptr);
 
             for (size_t mesh_idx : processing.second)
-                generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage, throw_on_cancel);
+                generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, 
+                    move_bounds, top_contacts, interface_layers, base_interface_layers, layer_storage, throw_on_cancel);
             auto t_gen = std::chrono::high_resolution_clock::now();
 
     #ifdef TREESUPPORT_DEBUG_SVG
@@ -4587,12 +4670,24 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
                     bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
             else {
                 assert(print_object.config().support_material_style == smsOrganic);
-                std::vector<Polygons> support_layer_storage = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel);
-                std::vector<Polygons> support_roof_storage(support_layer_storage.size());
-                finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
-                    bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
+                draw_branches(
+                    *print.get_object(processing.second.front()), volumes, config, move_bounds, 
+                    bottom_contacts, top_contacts, intermediate_layers, layer_storage, 
+                    throw_on_cancel);
             }
 
+            auto remove_undefined_layers = [](SupportGeneratorLayersPtr& layers) {
+                layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
+            };
+            remove_undefined_layers(bottom_contacts);
+            remove_undefined_layers(top_contacts);
+            remove_undefined_layers(interface_layers);
+            remove_undefined_layers(base_interface_layers);
+            remove_undefined_layers(intermediate_layers);
+
+            std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params,
+                bottom_contacts, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
+
             auto t_draw = std::chrono::high_resolution_clock::now();
             auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
             auto dur_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_gen - t_precalc).count();
@@ -4618,18 +4713,10 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
                 continue;
         }
 
-        auto remove_undefined_layers = [](SupportGeneratorLayersPtr &layers) {
-            layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
-        };
-        remove_undefined_layers(bottom_contacts);
-        remove_undefined_layers(top_contacts);
-        remove_undefined_layers(intermediate_layers);
-
         // Produce the support G-code.
         // Used by both classic and tree supports.
-        SupportParameters support_params(print_object);
-        SupportGeneratorLayersPtr interface_layers, base_interface_layers;
-        SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
+        SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), 
+            top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
 #if 1 //#ifdef SLIC3R_DEBUG
         SupportGeneratorLayersPtr layers_sorted =
 #endif // SLIC3R_DEBUG
diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp
index 2ed5d50ec..899f02724 100644
--- a/src/libslic3r/Support/TreeSupport.hpp
+++ b/src/libslic3r/Support/TreeSupport.hpp
@@ -364,10 +364,6 @@ public:
      * \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
      */
     size_t z_distance_bottom_layers;
-    /*!
-     * \brief used for performance optimization at the support floor. Should have no impact on the resulting tree.
-     */
-    size_t performance_interface_skip_layers;
     /*!
      * \brief User specified angles for the support infill.
      */

From c838fc92fc1ebb19791e003dd4481f21d5ee12c8 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Mon, 8 May 2023 10:25:28 +0200
Subject: [PATCH 2/3] Follow-up to 1e7a3216ca87d9fdeaa5b12f813e5900a77d49bc WIP
 Organic supports intefaces: bugfixes

---
 src/libslic3r/Support/SupportCommon.cpp | 37 +++++++++++++------------
 src/libslic3r/Support/TreeSupport.cpp   | 13 +++++++--
 2 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp
index 4978b9e40..951ae740d 100644
--- a/src/libslic3r/Support/SupportCommon.cpp
+++ b/src/libslic3r/Support/SupportCommon.cpp
@@ -137,7 +137,8 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
     if (! intermediate_layers.empty() && support_params.has_interfaces()) {
         // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
         BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
-        const bool snug_supports = config.support_material_style.value != smsGrid;
+        const bool snug_supports   = config.support_material_style.value == smsSnug;
+        const bool smooth_supports = config.support_material_style.value != smsGrid;
         SupportGeneratorLayersPtr &interface_layers       = base_and_interface_layers.first;
         SupportGeneratorLayersPtr &base_interface_layers  = base_and_interface_layers.second;
         interface_layers.assign(intermediate_layers.size(), nullptr);
@@ -147,7 +148,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
         const auto minimum_island_radius = support_params.support_material_interface_flow.scaled_spacing() / support_params.interface_density;
         const auto closing_distance      = smoothing_distance; // scaled<float>(config.support_material_closing_radius.value);
         // Insert a new layer into base_interface_layers, if intersection with base exists.
-        auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius](
+        auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius](
                 SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, 
                 const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
             bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty();
@@ -156,14 +157,16 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
             append(bottom, std::move(top));
             // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners).
             bottom = intersection(
-                snug_supports ?
+                smooth_supports ?
                     smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
                     union_safety_offset(std::move(bottom)),
                 intermediate_layer.polygons);
-            if (has_top_interface)
+            if (has_top_interface) {
                 // Don't trim the precomputed Organic supports top interface with base layer
                 // as the precomputed top interface likely expands over multiple tree tips.
                 bottom = union_(std::move(top_interface_layer->polygons), bottom);
+                top_interface_layer->polygons.clear();
+            }
             if (! bottom.empty()) {
                 //FIXME Remove non-printable tiny islands, let them be printed using the base support.
                 //bottom = opening(std::move(bottom), minimum_island_radius);
@@ -253,13 +256,8 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
                     auto resolve_same_layer = [](SupportGeneratorLayersPtr &layers, int &idx, coordf_t print_z) -> SupportGeneratorLayer* {
                         if (! layers.empty()) {
                             idx = idx_higher_or_equal(layers, idx, [print_z](const SupportGeneratorLayer *layer) { return layer->print_z > print_z - EPSILON; });
-                            if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON) {
-                                SupportGeneratorLayer *l = layers[idx];
-                                // Remove the layer from the source container, as it will be consumed here: It will be merged
-                                // with the newly produced interfaces.
-                                layers[idx] = nullptr;
-                                return l;
-                            }
+                            if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON)
+                                return layers[idx];
                         }
                         return nullptr;
                     };
@@ -284,11 +282,14 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
         // Compress contact_out, remove the nullptr items.
         // The parallel_for above may not have merged all the interface and base_interface layers
         // generated by the Organic supports code, do it here.
-        auto merge_remove_nulls = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) {
-            size_t nonzeros = std::count_if(in1.begin(), in1.end(), [](auto *l) { return l != nullptr; }) +
-                              std::count_if(in2.begin(), in2.end(), [](auto *l) { return l != nullptr; });
-            remove_nulls(in1);
-            remove_nulls(in2);
+        auto merge_remove_empty = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) {
+            auto remove_empty = [](SupportGeneratorLayersPtr &vec) {
+                vec.erase(
+                    std::remove_if(vec.begin(), vec.end(), [](const SupportGeneratorLayer *ptr) { return ptr == nullptr || ptr->polygons.empty(); }),
+                    vec.end());
+            };
+            remove_empty(in1);
+            remove_empty(in2);
             if (in2.empty())
                 return std::move(in1);
             else if (in1.empty())
@@ -299,8 +300,8 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
                 return std::move(out);
             }
         };
-        interface_layers      = merge_remove_nulls(interface_layers,      top_interface_layers);
-        base_interface_layers = merge_remove_nulls(base_interface_layers, top_base_interface_layers);
+        interface_layers      = merge_remove_empty(interface_layers,      top_interface_layers);
+        base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers);
         BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
     }
     
diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp
index dfeb3fdf9..e27661384 100644
--- a/src/libslic3r/Support/TreeSupport.cpp
+++ b/src/libslic3r/Support/TreeSupport.cpp
@@ -1119,7 +1119,7 @@ public:
     void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
     {
         std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-        this->add_roof_unguarded(std::move(overhang_areas), 0, dtt_roof);
+        this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers));
     }
 
     // called by sample_overhang_area()
@@ -1204,6 +1204,8 @@ public:
 private:
     void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
     {
+        assert(support_parameters.has_top_contacts);
+        assert(dtt_roof <= support_parameters.num_top_interface_layers);
         SupportGeneratorLayersPtr &layers =
             dtt_roof == 0 ? this->top_contacts :
             dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces;
@@ -4413,8 +4415,13 @@ static void draw_branches(
                                         bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
                                     if (area(this_bottom_contacts) < support_area_min)
                                         bottom_extra_slices.pop_back();
-                                    else if (config.settings.support_floor_layers > 0)
-                                        bottom_contacts.emplace_back(std::move(this_bottom_contacts));
+                                    else {
+                                        // At least a fraction of the tree bottom is considered to be supported.
+                                        if (config.settings.support_floor_layers > 0)
+                                            // Turn this fraction of the tree bottom into a contact layer.
+                                            bottom_contacts.emplace_back(std::move(this_bottom_contacts));
+                                        break;
+                                    }
                                 }
                                 if (config.settings.support_floor_layers > 0)
                                     for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i)

From 9d495f2413c215c80de9eead736e78c1c9142453 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 9 May 2023 09:46:27 +0200
Subject: [PATCH 3/3] Organic supports: Refactoring for adding interfaces to
 tree tips.

---
 src/libslic3r/Support/TreeSupport.cpp | 549 ++++++++++++++------------
 1 file changed, 290 insertions(+), 259 deletions(-)

diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp
index e27661384..0eb009c77 100644
--- a/src/libslic3r/Support/TreeSupport.cpp
+++ b/src/libslic3r/Support/TreeSupport.cpp
@@ -997,11 +997,245 @@ inline SupportGeneratorLayer& layer_allocate(
     return layer_initialize(layer, slicing_params, config, layer_idx);
 }
 
+using SupportElements = std::deque<SupportElement>;
+
+// Used by generate_initial_areas() in parallel by multiple layers.
+class InterfacePlacer {
+public:
+    InterfacePlacer(
+        const SlicingParameters         &slicing_parameters, 
+        const SupportParameters         &support_parameters,
+        const TreeSupportSettings       &config, 
+        SupportGeneratorLayerStorage    &layer_storage, 
+        SupportGeneratorLayersPtr       &top_contacts,
+        SupportGeneratorLayersPtr       &top_interfaces,
+        SupportGeneratorLayersPtr       &top_base_interfaces) 
+    :
+        slicing_parameters(slicing_parameters), support_parameters(support_parameters), config(config),
+        layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces)  
+    {}
+    InterfacePlacer(const InterfacePlacer& rhs) :
+        slicing_parameters(rhs.slicing_parameters), support_parameters(rhs.support_parameters), config(rhs.config),
+        layer_storage(rhs.layer_storage), top_contacts(rhs.top_contacts), top_interfaces(rhs.top_interfaces), top_base_interfaces(rhs.top_base_interfaces) 
+    {}
+
+    const SlicingParameters    &slicing_parameters;
+    const SupportParameters    &support_parameters;
+    const TreeSupportSettings  &config;
+    SupportGeneratorLayersPtr&  top_contacts_mutable() { return this->top_contacts; }
+
+public:
+    // Insert the contact layer and some of the inteface and base interface layers below.
+    void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx)
+    {
+        if (! new_roofs.empty()) {
+            std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
+            for (size_t idx = 0; idx < new_roofs.size(); ++ idx)
+                if (! new_roofs[idx].empty())
+                    add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx);
+        }
+    }
+
+    void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip)
+    {
+        std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
+        add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip);
+    }
+
+    // called by sample_overhang_area()
+    void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
+    {
+        std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
+        this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers));
+    }
+
+    void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
+    {
+        assert(support_parameters.has_top_contacts);
+        assert(dtt_roof <= support_parameters.num_top_interface_layers);
+        SupportGeneratorLayersPtr &layers =
+            dtt_roof == 0 ? this->top_contacts :
+            dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces;
+        SupportGeneratorLayer*& l = layers[insert_layer_idx];
+        if (l == nullptr)
+            l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface, 
+                    slicing_parameters, config, insert_layer_idx);
+        // will be unioned in finalize_interface_and_support_areas()
+        append(l->polygons, std::move(new_roofs));
+    }
+
+private:
+    // Outputs
+    SupportGeneratorLayerStorage                       &layer_storage;
+    SupportGeneratorLayersPtr                          &top_contacts;
+    SupportGeneratorLayersPtr                          &top_interfaces;
+    SupportGeneratorLayersPtr                          &top_base_interfaces;
+
+    // Mutexes, guards
+    std::mutex                                          m_mutex_layer_storage;
+};
+
+class RichInterfacePlacer : public InterfacePlacer {
+public:
+    RichInterfacePlacer(
+        const InterfacePlacer        &interface_placer,
+        const TreeModelVolumes       &volumes,
+        bool                          force_tip_to_roof,
+        size_t                        num_support_layers,
+        std::vector<SupportElements> &move_bounds)
+    :
+        InterfacePlacer(interface_placer),
+        volumes(volumes), force_tip_to_roof(force_tip_to_roof), move_bounds(move_bounds)
+    {
+        m_already_inserted.assign(num_support_layers, {});
+        this->min_xy_dist = this->config.xy_distance > this->config.xy_min_distance;
+    }
+    const TreeModelVolumes                             &volumes;
+    // Radius of the tree tip is large enough to be covered by an interface.
+    const bool                                          force_tip_to_roof;
+    bool                                                min_xy_dist;
+
+public:
+    // called by sample_overhang_area()
+    void add_points_along_lines(
+        // Insert points (tree tips or top contact interfaces) along these lines.
+        LineInformations    lines, 
+        // Start at this layer.
+        LayerIndex          insert_layer_idx,
+        // Insert this number of interface layers.
+        size_t              roof_tip_layers,
+        // True if an interface is already generated above these lines.
+        size_t              supports_roof_layers,
+        // The element tries to not move until this dtt is reached.
+        size_t              dont_move_until)
+    {
+        validate_range(lines);
+        // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible
+        size_t dtt_roof_tip;
+        for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) {
+            size_t this_layer_idx = insert_layer_idx - dtt_roof_tip;
+            auto evaluateRoofWillGenerate = [&](const std::pair<Point, LineStatus> &p) {
+                //FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time!
+    #if 0
+                Polygon roof_circle;
+                for (Point corner : base_circle)
+                    roof_circle.points.emplace_back(p.first + corner * config.min_radius);
+                return !generate_support_infill_lines({ roof_circle }, config, true, insert_layer_idx - dtt_roof_tip, config.support_roof_line_distance).empty();
+    #else
+                return true;
+    #endif
+            };
+
+            {
+                std::pair<LineInformations, LineInformations> split =
+                    // keep all lines that are still valid on the next layer
+                    split_lines(lines, [this, this_layer_idx](const std::pair<Point, LineStatus> &p)
+                        { return evaluate_point_for_next_layer_function(volumes, config, this_layer_idx, p); });
+                LineInformations points = std::move(split.second);
+                // Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points.
+                split = split_lines(split.first, evaluateRoofWillGenerate);
+                lines = std::move(split.first);
+                append(points, split.second);
+                // add all points that would not be valid
+                for (const LineInformation &line : points)
+                    for (const std::pair<Point, LineStatus> &point_data : line)
+                        add_point_as_influence_area(point_data, this_layer_idx, 
+                            // don't move until
+                            roof_tip_layers - dtt_roof_tip,
+                            // supports roof
+                            dtt_roof_tip + supports_roof_layers > 0,
+                            // disable ovalization
+                            false);
+            }
+
+            // add all tips as roof to the roof storage
+            Polygons new_roofs;
+            for (const LineInformation &line : lines)
+                //FIXME sweep the tip radius along the line?
+                for (const std::pair<Point, LineStatus> &p : line) {
+                    Polygon roof_circle{ m_base_circle };
+                    roof_circle.scale(config.min_radius / m_base_radius);
+                    roof_circle.translate(p.first);
+                    new_roofs.emplace_back(std::move(roof_circle));
+                }
+            this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
+        }
+
+        for (const LineInformation &line : lines) {
+            // If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern.
+            // Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width
+            bool disable_ovalistation = config.min_radius < 3 * config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5;
+            for (const std::pair<Point, LineStatus> &point_data : line)
+                add_point_as_influence_area(point_data, insert_layer_idx - dtt_roof_tip,
+                    // don't move until
+                    dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
+                    // supports roof
+                    dtt_roof_tip + supports_roof_layers > 0,
+                    disable_ovalistation);
+        }
+    }
+
+private:
+    // called by this->add_points_along_lines()
+    void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation)
+    {
+        bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
+        bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
+        bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
+        if (! config.support_rests_on_model && ! to_bp) {
+            BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point";
+            tree_supports_show_error("Unable to add tip. Some overhang may not be supported correctly."sv, true);
+            return;
+        }
+        Polygons circle{ m_base_circle };
+        circle.front().translate(p.first);
+        {
+            Point hash_pos = p.first / ((config.min_radius + 1) / 10);
+            std::lock_guard<std::mutex> critical_section_movebounds(m_mutex_movebounds);
+            if (!m_already_inserted[insert_layer].count(hash_pos)) {
+                // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
+                m_already_inserted[insert_layer].emplace(hash_pos);
+                static constexpr const size_t dtt = 0;
+                SupportElementState state;
+                state.target_height = insert_layer;
+                state.target_position = p.first;
+                state.next_position = p.first;
+                state.layer_idx = insert_layer;
+                state.effective_radius_height = dtt;
+                state.to_buildplate = to_bp;
+                state.distance_to_top = dtt;
+                state.result_on_layer = p.first;
+                assert(state.result_on_layer_is_set());
+                state.increased_to_model_radius = 0;
+                state.to_model_gracious = gracious;
+                state.elephant_foot_increases = 0;
+                state.use_min_xy_dist = min_xy_dist;
+                state.supports_roof = roof;
+                state.dont_move_until = dont_move_until;
+                state.can_use_safe_radius = safe_radius;
+                state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0;
+                state.skip_ovalisation = skip_ovalisation;
+                move_bounds[insert_layer].emplace_back(state, std::move(circle));
+            }
+        }
+    }
+
+    // Outputs
+    std::vector<SupportElements>                       &move_bounds;
+
+    // Temps
+    static constexpr const auto                         m_base_radius = scaled<int>(0.01);
+    const Polygon                                       m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
+    
+    // Mutexes, guards
+    std::mutex                                          m_mutex_movebounds;
+    std::vector<std::unordered_set<Point, PointHash>>   m_already_inserted;
+};
+
 int generate_raft_contact(
     const PrintObject               &print_object,
     const TreeSupportSettings       &config,
-    SupportGeneratorLayersPtr       &top_contacts,
-    SupportGeneratorLayerStorage    &layer_storage)
+    InterfacePlacer                 &interface_placer)
 {
     int raft_contact_layer_idx = -1;
     if (print_object.has_raft() && print_object.layer_count() > 0) {
@@ -1012,17 +1246,13 @@ int generate_raft_contact(
         while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON)
             -- raft_contact_layer_idx;
         // Create the raft contact layer.
-        SupportGeneratorLayer &raft_contact_layer = layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx);
-        top_contacts[raft_contact_layer_idx] = &raft_contact_layer;
         const ExPolygons &lslices   = print_object.get_layer(0)->lslices;
         double            expansion = print_object.config().raft_expansion.value;
-        raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices);
+        interface_placer.add_roof_unguarded(expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices), raft_contact_layer_idx, 0);
     }
     return raft_contact_layer_idx;
 }
 
-using SupportElements = std::deque<SupportElement>;
-
 void finalize_raft_contact(
     const PrintObject               &print_object,
     const int                        raft_contact_layer_idx,
@@ -1076,215 +1306,6 @@ void finalize_raft_contact(
     }
 }
 
-// Used by generate_initial_areas() in parallel by multiple layers.
-class InterfacePlacer {
-public:
-    InterfacePlacer(
-        const SlicingParameters &slicing_parameters, 
-        const SupportParameters &support_parameters,
-        const TreeModelVolumes &volumes, const TreeSupportSettings &config, 
-        bool force_tip_to_roof, size_t num_support_layers,
-        std::vector<SupportElements> &move_bounds, 
-        SupportGeneratorLayerStorage &layer_storage, 
-        SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interfaces, SupportGeneratorLayersPtr &top_base_interfaces) 
-    :
-        slicing_parameters(slicing_parameters), support_parameters(support_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof),
-        move_bounds(move_bounds), 
-        layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces)  {
-        m_already_inserted.assign(num_support_layers, {});
-        this->min_xy_dist = config.xy_distance > config.xy_min_distance;
-    }
-    const SlicingParameters                            &slicing_parameters;
-    const SupportParameters                            &support_parameters;
-    const TreeModelVolumes                             &volumes;
-    const TreeSupportSettings                          &config;
-    // Radius of the tree tip is large enough to be covered by an interface.
-    const bool                                          force_tip_to_roof;
-    bool                                                min_xy_dist;
-    
-public:
-    // called by sample_overhang_area()
-    // Insert the contact layer and some of the inteface and base interface layers below.
-    void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx)
-    {
-        if (! new_roofs.empty()) {
-            std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-            for (size_t idx = 0; idx < new_roofs.size(); ++ idx)
-                if (! new_roofs[idx].empty())
-                    add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx);
-        }
-    }
-
-    // called by sample_overhang_area()
-    void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
-    {
-        std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-        this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers));
-    }
-
-    // called by sample_overhang_area()
-    void add_points_along_lines(
-        // Insert points (tree tips or top contact interfaces) along these lines.
-        LineInformations    lines, 
-        // Start at this layer.
-        LayerIndex          insert_layer_idx,
-        // Insert this number of interface layers.
-        size_t              roof_tip_layers,
-        // True if an interface is already generated above these lines.
-        size_t              supports_roof_layers,
-        // The element tries to not move until this dtt is reached.
-        size_t              dont_move_until)
-    {
-        validate_range(lines);
-        // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible
-        size_t dtt_roof_tip;
-        for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) {
-            size_t this_layer_idx = insert_layer_idx - dtt_roof_tip;
-            auto evaluateRoofWillGenerate = [&](const std::pair<Point, LineStatus> &p) {
-                //FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time!
-    #if 0
-                Polygon roof_circle;
-                for (Point corner : base_circle)
-                    roof_circle.points.emplace_back(p.first + corner * config.min_radius);
-                return !generate_support_infill_lines({ roof_circle }, config, true, insert_layer_idx - dtt_roof_tip, config.support_roof_line_distance).empty();
-    #else
-                return true;
-    #endif
-            };
-
-            {
-                std::pair<LineInformations, LineInformations> split =
-                    // keep all lines that are still valid on the next layer
-                    split_lines(lines, [this, this_layer_idx](const std::pair<Point, LineStatus> &p)
-                        { return evaluate_point_for_next_layer_function(volumes, config, this_layer_idx, p); });
-                LineInformations points = std::move(split.second);
-                // Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points.
-                split = split_lines(split.first, evaluateRoofWillGenerate);
-                lines = std::move(split.first);
-                append(points, split.second);
-                // add all points that would not be valid
-                for (const LineInformation &line : points)
-                    for (const std::pair<Point, LineStatus> &point_data : line)
-                        add_point_as_influence_area(point_data, this_layer_idx, 
-                            // don't move until
-                            roof_tip_layers - dtt_roof_tip,
-                            // supports roof
-                            dtt_roof_tip > 0,
-                            // disable ovalization
-                            false);
-            }
-
-            // add all tips as roof to the roof storage
-            Polygons new_roofs;
-            for (const LineInformation &line : lines)
-                //FIXME sweep the tip radius along the line?
-                for (const std::pair<Point, LineStatus> &p : line) {
-                    Polygon roof_circle{ m_base_circle };
-                    roof_circle.scale(config.min_radius / m_base_radius);
-                    roof_circle.translate(p.first);
-                    new_roofs.emplace_back(std::move(roof_circle));
-                }
-            this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
-        }
-
-        for (const LineInformation &line : lines) {
-            // If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern.
-            // Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width
-            bool disable_ovalistation = config.min_radius < 3 * config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5;
-            for (const std::pair<Point, LineStatus> &point_data : line)
-                add_point_as_influence_area(point_data, insert_layer_idx - dtt_roof_tip,
-                    // don't move until
-                    dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
-                    // supports roof
-                    dtt_roof_tip + supports_roof_layers > 0,
-                    disable_ovalistation);
-        }
-    }
-
-private:
-    void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
-    {
-        assert(support_parameters.has_top_contacts);
-        assert(dtt_roof <= support_parameters.num_top_interface_layers);
-        SupportGeneratorLayersPtr &layers =
-            dtt_roof == 0 ? this->top_contacts :
-            dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces;
-        SupportGeneratorLayer*& l = layers[insert_layer_idx];
-        if (l == nullptr)
-            l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface, 
-                    slicing_parameters, config, insert_layer_idx);
-        // will be unioned in finalize_interface_and_support_areas()
-        append(l->polygons, std::move(new_roofs));
-    }
-
-    // called by this->add_points_along_lines()
-    void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip)
-    {
-        std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
-        add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip);
-    }
-
-    // called by this->add_points_along_lines()
-    void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation)
-    {
-        bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
-        bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
-        bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
-        if (! config.support_rests_on_model && ! to_bp) {
-            BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point";
-            tree_supports_show_error("Unable to add tip. Some overhang may not be supported correctly."sv, true);
-            return;
-        }
-        Polygons circle{ m_base_circle };
-        circle.front().translate(p.first);
-        {
-            Point hash_pos = p.first / ((config.min_radius + 1) / 10);
-            std::lock_guard<std::mutex> critical_section_movebounds(m_mutex_movebounds);
-            if (!m_already_inserted[insert_layer].count(hash_pos)) {
-                // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
-                m_already_inserted[insert_layer].emplace(hash_pos);
-                static constexpr const size_t dtt = 0;
-                SupportElementState state;
-                state.target_height = insert_layer;
-                state.target_position = p.first;
-                state.next_position = p.first;
-                state.layer_idx = insert_layer;
-                state.effective_radius_height = dtt;
-                state.to_buildplate = to_bp;
-                state.distance_to_top = dtt;
-                state.result_on_layer = p.first;
-                assert(state.result_on_layer_is_set());
-                state.increased_to_model_radius = 0;
-                state.to_model_gracious = gracious;
-                state.elephant_foot_increases = 0;
-                state.use_min_xy_dist = min_xy_dist;
-                state.supports_roof = roof;
-                state.dont_move_until = dont_move_until;
-                state.can_use_safe_radius = safe_radius;
-                state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0;
-                state.skip_ovalisation = skip_ovalisation;
-                move_bounds[insert_layer].emplace_back(state, std::move(circle));
-            }
-        }
-    }
-
-    // Outputs
-    std::vector<SupportElements>                       &move_bounds;
-    SupportGeneratorLayerStorage                       &layer_storage;
-    SupportGeneratorLayersPtr                          &top_contacts;
-    SupportGeneratorLayersPtr                          &top_interfaces;
-    SupportGeneratorLayersPtr                          &top_base_interfaces;
-
-    // Temps
-    static constexpr const auto                         m_base_radius = scaled<int>(0.01);
-    const Polygon                                       m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
-    
-    // Mutexes, guards
-    std::mutex                                          m_mutex_movebounds;
-    std::mutex                                          m_mutex_layer_storage;
-    std::vector<std::unordered_set<Point, PointHash>>   m_already_inserted;
-};
-
 // Called by generate_initial_areas(), used in parallel by multiple layers.
 // Produce
 // 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
@@ -1309,7 +1330,7 @@ void sample_overhang_area(
     // Configuration classes
     const TreeSupportMeshGroupSettings  &mesh_group_settings,
     // Configuration & Output
-    InterfacePlacer                     &interface_placer)
+    RichInterfacePlacer                 &interface_placer)
 {
     // Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area 
     // is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument 
@@ -1447,17 +1468,11 @@ static void generate_initial_areas(
     const TreeSupportSettings       &config,
     const std::vector<Polygons>     &overhangs,
     std::vector<SupportElements>    &move_bounds,
-    SupportGeneratorLayersPtr       &top_contacts,
-    SupportGeneratorLayersPtr       &top_interfaces,
-    SupportGeneratorLayersPtr       &top_base_interfaces,
-    SupportGeneratorLayerStorage    &layer_storage,
+    InterfacePlacer                 &interface_placer,
     std::function<void()>            throw_on_cancel)
 {
     using                           AvoidanceType = TreeModelVolumes::AvoidanceType;
     TreeSupportMeshGroupSettings    mesh_group_settings(print_object);
-    SupportParameters               support_params(print_object);
-    support_params.with_sheath = true;
-    support_params.support_density = 0;
 
     // To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below
     const size_t z_distance_delta = config.z_distance_top_layers + 1;
@@ -1487,7 +1502,7 @@ static void generate_initial_areas(
         ;
     const size_t  num_support_roof_layers = mesh_group_settings.support_roof_layers;
     const bool    roof_enabled        = num_support_roof_layers > 0;
-    const bool    force_tip_to_roof   = roof_enabled && sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area;
+    const bool    force_tip_to_roof   = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area);
     // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point 
     // may cause extra material and time cost.  Could also be an user setting or differently calculated. Idea is that if an overhang 
     // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. 
@@ -1513,7 +1528,7 @@ static void generate_initial_areas(
         const size_t num_raft_layers     = config.raft_layers.size();
         const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1);
         num_support_layers  = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta)));
-        raft_contact_layer_idx = generate_raft_contact(print_object, config, top_contacts, layer_storage);
+        raft_contact_layer_idx = generate_raft_contact(print_object, config, interface_placer);
         // Enumerate layers for which the support tips may be generated from overhangs above.
         raw_overhangs.reserve(num_support_layers - first_support_layer);
         for (size_t layer_idx = first_support_layer; layer_idx < num_support_layers; ++ layer_idx)
@@ -1521,14 +1536,12 @@ static void generate_initial_areas(
                 raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
     }
 
-    InterfacePlacer interface_placer{ print_object.slicing_parameters(), support_params, volumes, config, force_tip_to_roof, num_support_layers,
-        // Outputs
-        move_bounds, layer_storage, top_contacts, top_interfaces, top_base_interfaces };
+    RichInterfacePlacer rich_interface_placer{ interface_placer, volumes, force_tip_to_roof, num_support_layers, move_bounds };
 
     tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
-        [&volumes, &config, &raw_overhangs, &mesh_group_settings, &support_params,
+        [&volumes, &config, &raw_overhangs, &mesh_group_settings,
          min_xy_dist, force_tip_to_roof, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag,
-         &interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
+         &rich_interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
         for (size_t raw_overhang_idx = range.begin(); raw_overhang_idx < range.end(); ++ raw_overhang_idx) {
             size_t           layer_idx    = raw_overhangs[raw_overhang_idx].first;
             const Polygons  &overhang_raw = *raw_overhangs[raw_overhang_idx].second;
@@ -1620,7 +1633,7 @@ static void generate_initial_areas(
                         LineInformations fresh_valid_points = convert_lines_to_internal(volumes, config, convert_internal_to_lines(split.second), layer_idx - lag_ctr);
                         validate_range(fresh_valid_points);
 
-                        interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0);
+                        rich_interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0);
                     }
                 }
 #endif
@@ -1638,7 +1651,7 @@ static void generate_initial_areas(
                 //check_self_intersections(overhang_regular, "overhang_regular3");
                 for (ExPolygon &roof_part : union_ex(overhang_roofs)) {
                     sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length,
-                        mesh_group_settings, interface_placer);
+                        mesh_group_settings, rich_interface_placer);
                     throw_on_cancel();
                 }
             }
@@ -1649,13 +1662,13 @@ static void generate_initial_areas(
             for (ExPolygon &support_part : union_ex(overhang_regular)) {
                 sample_overhang_area(to_polygons(std::move(support_part)), 
                     false, layer_idx, num_support_roof_layers, connect_length,
-                    mesh_group_settings, interface_placer);
+                    mesh_group_settings, rich_interface_placer);
                 throw_on_cancel();
             }
         }
     });
 
-    finalize_raft_contact(print_object, raft_contact_layer_idx, top_contacts, move_bounds);
+    finalize_raft_contact(print_object, raft_contact_layer_idx, interface_placer.top_contacts_mutable(), move_bounds);
 }
 
 static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max())
@@ -4172,6 +4185,7 @@ static void draw_branches(
     // I/O:
     SupportGeneratorLayersPtr       &bottom_contacts,
     SupportGeneratorLayersPtr       &top_contacts,
+    InterfacePlacer                 &interface_placer,
 
     // Output:
     SupportGeneratorLayersPtr       &intermediate_layers,
@@ -4346,7 +4360,7 @@ static void draw_branches(
     mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
 
     tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
-        [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
+        [&trees, &volumes, &config, &slicing_params, &move_bounds, &interface_placer, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
             indexed_triangle_set    partial_mesh;
             std::vector<float>      slice_z;
             std::vector<Polygons>   bottom_contacts;
@@ -4434,10 +4448,20 @@ static void draw_branches(
                                     *it_dst ++ = std::move(it_src->polygons);
                             }
                         }
-                        if (branch.has_tip) {
+                        
+#if 0
+                        //FIXME branch.has_tip seems to not be reliable.
+                        if (branch.has_tip && interface_placer.support_parameters.has_top_contacts)
                             // Add top slices to top contacts / interfaces / base interfaces.
-                            //slices;
-                        }
+                            for (int i = int(branch.path.size()) - 1; i >= 0; -- i) {
+                                const SupportElement &el = *branch.path[i];
+                                if (el.state.missing_roof_layers == 0)
+                                    break;
+                                //FIXME Move or not?
+                                interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx,
+                                    interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers);
+                            }
+#endif
                     }
 
                     layer_begin += LayerIndex(num_empty);
@@ -4619,34 +4643,44 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
         std::vector<Polygons>        overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
 
         // ### Precalculate avoidances, collision etc.
-        
+        size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
+        bool   has_support = num_support_layers > 0;
+        num_support_layers = std::max(num_support_layers, config.raft_layers.size());
+
+        SupportParameters            support_params(print_object);
+        support_params.with_sheath = true;
+        support_params.support_density = 0;
+
         SupportGeneratorLayerStorage layer_storage;
         SupportGeneratorLayersPtr    top_contacts;
         SupportGeneratorLayersPtr    bottom_contacts;
         SupportGeneratorLayersPtr    interface_layers;
         SupportGeneratorLayersPtr    base_interface_layers;
-        SupportGeneratorLayersPtr    intermediate_layers;
+        SupportGeneratorLayersPtr    intermediate_layers(num_support_layers, nullptr);
+        if (support_params.has_top_contacts)
+            top_contacts.assign(num_support_layers, nullptr);
+        if (support_params.has_bottom_contacts)
+            bottom_contacts.assign(num_support_layers, nullptr);
+        if (support_params.has_interfaces())
+            interface_layers.assign(num_support_layers, nullptr);
+        if (support_params.has_base_interfaces())
+            base_interface_layers.assign(num_support_layers, nullptr);
 
-        SupportParameters support_params(print_object);
-
-        if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
-            num_support_layers > 0) {
+        InterfacePlacer              interface_placer{
+            print_object.slicing_parameters(), support_params, config,
+            // Outputs
+            layer_storage, top_contacts, interface_layers, base_interface_layers };
 
+        if (has_support) {
             auto t_precalc = std::chrono::high_resolution_clock::now();
 
             // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas
             std::vector<SupportElements> move_bounds(num_support_layers);
 
             // ### Place tips of the support tree
-            bottom_contacts      .assign(num_support_layers, nullptr);
-            top_contacts         .assign(num_support_layers, nullptr);
-            interface_layers     .assign(num_support_layers, nullptr);
-            base_interface_layers.assign(num_support_layers, nullptr);
-            intermediate_layers  .assign(num_support_layers, nullptr);
-
             for (size_t mesh_idx : processing.second)
                 generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, 
-                    move_bounds, top_contacts, interface_layers, base_interface_layers, layer_storage, throw_on_cancel);
+                    move_bounds, interface_placer, throw_on_cancel);
             auto t_gen = std::chrono::high_resolution_clock::now();
 
     #ifdef TREESUPPORT_DEBUG_SVG
@@ -4679,7 +4713,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
                 assert(print_object.config().support_material_style == smsOrganic);
                 draw_branches(
                     *print.get_object(processing.second.front()), volumes, config, move_bounds, 
-                    bottom_contacts, top_contacts, intermediate_layers, layer_storage, 
+                    bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, 
                     throw_on_cancel);
             }
 
@@ -4713,12 +4747,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
     //            BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)";
             
             move_bounds.clear();
-        } else {
-            top_contacts.assign(config.raft_layers.size(), nullptr);
-            if (generate_raft_contact(print_object, config, top_contacts, layer_storage) < 0)
-                // No raft.
-                continue;
-        }
+        } else if (generate_raft_contact(print_object, config, interface_placer) < 0)
+            // No raft.
+            continue;
 
         // Produce the support G-code.
         // Used by both classic and tree supports.