diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp
index 59ad20c72..4637aa761 100644
--- a/src/libslic3r/SLAPrintSteps.cpp
+++ b/src/libslic3r/SLAPrintSteps.cpp
@@ -84,17 +84,17 @@ SLAPrint::Steps::Steps(SLAPrint *print)
 void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin o)
 {
     if (o == soSupport && !po.m_supportdata) return;
-    
+
     auto faded_lyrs = size_t(po.m_config.faded_layers.getInt());
     double min_w = m_print->m_printer_config.elefant_foot_min_width.getFloat() / 2.;
     double start_efc = m_print->m_printer_config.elefant_foot_compensation.getFloat();
-    
+
     double doffs = m_print->m_printer_config.absolute_correction.getFloat();
     coord_t clpr_offs = scaled(doffs);
-    
+
     faded_lyrs = std::min(po.m_slice_index.size(), faded_lyrs);
     size_t faded_lyrs_efc = std::max(size_t(1), faded_lyrs - 1);
-    
+
     auto efc = [start_efc, faded_lyrs_efc](size_t pos) {
         return (faded_lyrs_efc - pos) * start_efc / faded_lyrs_efc;
     };
@@ -102,13 +102,13 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin
     std::vector<ExPolygons> &slices = o == soModel ?
                                           po.m_model_slices :
                                           po.m_supportdata->support_slices;
-    
+
     if (clpr_offs != 0) for (size_t i = 0; i < po.m_slice_index.size(); ++i) {
         size_t idx = po.m_slice_index[i].get_slice_idx(o);
         if (idx < slices.size())
             slices[idx] = offset_ex(slices[idx], float(clpr_offs));
     }
-    
+
     if (start_efc > 0.) for (size_t i = 0; i < faded_lyrs; ++i) {
         size_t idx = po.m_slice_index[i].get_slice_idx(o);
         if (idx < slices.size())
@@ -124,7 +124,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
         BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!";
         return;
     }
-    
+
     BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!";
 
     double thickness = po.m_config.hollowing_min_thickness.getFloat();
@@ -169,16 +169,17 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
     sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior);
 
     TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed;
-    sla::remove_inside_triangles(mesh_view, *po.m_hollowing_data->interior);
+    mesh_view = po.transformed_mesh();
+    sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, sla::hfRemoveInsideTriangles);
 
     if (! needs_drilling) {
         BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
         return;
     }
-    
+
     BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
     sla::DrainHoles drainholes = po.transformed_drainhole_points();
-    
+
     std::uniform_real_distribution<float> dist(0., float(EPSILON));
     auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({});
     for (sla::DrainHole holept : drainholes) {
@@ -190,12 +191,12 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
         auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
         MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m);
     }
-    
+
     if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal))
         throw Slic3r::SlicingError(L("Too many overlapping holes."));
-    
+
     auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh);
-    
+
     try {
         MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal);
         hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
@@ -215,11 +216,11 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
 // of it. In any case, the model and the supports have to be sliced in the
 // same imaginary grid (the height vector argument to TriangleMeshSlicer).
 void SLAPrint::Steps::slice_model(SLAPrintObject &po)
-{   
+{
     const TriangleMesh &mesh = po.get_mesh_to_slice();
 
     // We need to prepare the slice index...
-    
+
     double  lhd  = m_print->m_objects.front()->m_config.layer_height.getFloat();
     float   lh   = float(lhd);
     coord_t lhs  = scaled(lhd);
@@ -229,40 +230,40 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
     auto    minZf = float(minZ);
     coord_t minZs = scaled(minZ);
     coord_t maxZs = scaled(maxZ);
-    
+
     po.m_slice_index.clear();
-    
+
     size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs);
     po.m_slice_index.reserve(cap);
-    
+
     po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh);
-    
+
     for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs)
         po.m_slice_index.emplace_back(h, unscaled<float>(h) - lh / 2.f, lh);
-    
+
     // Just get the first record that is from the model:
     auto slindex_it =
         po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z)));
-    
+
     if(slindex_it == po.m_slice_index.end())
         //TRN To be shown at the status bar on SLA slicing error.
         throw Slic3r::RuntimeError(
             L("Slicing had to be stopped due to an internal error: "
               "Inconsistent slice index."));
-    
+
     po.m_model_height_levels.clear();
     po.m_model_height_levels.reserve(po.m_slice_index.size());
     for(auto it = slindex_it; it != po.m_slice_index.end(); ++it)
         po.m_model_height_levels.emplace_back(it->slice_level());
-    
+
     TriangleMeshSlicer slicer(&mesh);
-    
+
     po.m_model_slices.clear();
     float closing_r  = float(po.config().slice_closing_radius.value);
     auto  thr        = [this]() { m_print->throw_if_canceled(); };
     auto &slice_grid = po.m_model_height_levels;
     slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr);
-    
+
     sla::Interior *interior = po.m_hollowing_data ?
                                   po.m_hollowing_data->interior.get() :
                                   nullptr;
@@ -282,17 +283,17 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
                                   diff_ex(po.m_model_slices[i], slice);
                            });
     }
-    
+
     auto mit = slindex_it;
     for (size_t id = 0;
          id < po.m_model_slices.size() && mit != po.m_slice_index.end();
          id++) {
         mit->set_model_slice_idx(po, id); ++mit;
     }
-    
+
     // We apply the printer correction offset here.
     apply_printer_corrections(po, soModel);
-        
+
     if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool())
     {
         po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh));
@@ -305,22 +306,22 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
 {
     // If supports are disabled, we can skip the model scan.
     if(!po.m_config.supports_enable.getBool()) return;
-    
+
     const TriangleMesh &mesh = po.get_mesh_to_slice();
-    
+
     if (!po.m_supportdata)
         po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh));
-    
+
     const ModelObject& mo = *po.m_model_object;
-    
+
     BOOST_LOG_TRIVIAL(debug) << "Support point count "
                              << mo.sla_support_points.size();
-    
+
     // Unless the user modified the points or we already did the calculation,
     // we will do the autoplacement. Otherwise we will just blindly copy the
     // frontend data into the backend cache.
     if (mo.sla_points_status != sla::PointsStatus::UserModified) {
-        
+
         // calculate heights of slices (slices are calculated already)
         const std::vector<float>& heights = po.m_model_height_levels;
 
@@ -328,27 +329,27 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
         // calculated on slices, the algorithm then raycasts the points
         // so they actually lie on the mesh.
 //        po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points());
-        
+
         throw_if_canceled();
         sla::SupportPointGenerator::Config config;
         const SLAPrintObjectConfig& cfg = po.config();
-        
+
         // the density config value is in percents:
         config.density_relative = float(cfg.support_points_density_relative / 100.f);
         config.minimal_distance = float(cfg.support_points_minimal_distance);
         config.head_diameter    = float(cfg.support_head_front_diameter);
-        
+
         // scaling for the sub operations
         double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0;
         double init = current_status();
-        
+
         auto statuscb = [this, d, init](unsigned st)
         {
             double current = init + st * d;
             if(std::round(current_status()) < std::round(current))
                 report_status(current, OBJ_STEP_LABELS(slaposSupportPoints));
         };
-        
+
         // Construction of this object does the calculation.
         throw_if_canceled();
         sla::SupportPointGenerator auto_supports(
@@ -359,10 +360,10 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
         const std::vector<sla::SupportPoint>& points = auto_supports.output();
         throw_if_canceled();
         po.m_supportdata->pts = points;
-        
+
         BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
                                  << po.m_supportdata->pts.size();
-        
+
         // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
         // the update status to GLGizmoSlaSupports
         report_status(-1, L("Generating support points"),
@@ -377,9 +378,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
 void SLAPrint::Steps::support_tree(SLAPrintObject &po)
 {
     if(!po.m_supportdata) return;
-    
+
     sla::PadConfig pcfg = make_pad_cfg(po.m_config);
-    
+
     if (pcfg.embed_object)
         po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm);
 
@@ -389,15 +390,15 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
         remove_bottom_points(po.m_supportdata->pts,
                              float(po.m_supportdata->emesh.ground_level() + EPSILON));
     }
-    
+
     po.m_supportdata->cfg = make_support_cfg(po.m_config);
 //    po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points());
-    
+
     // scaling for the sub operations
     double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0;
     double init = current_status();
     sla::JobController ctl;
-    
+
     ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) {
         double current = init + st * d;
         if (std::round(current_status()) < std::round(current))
@@ -406,26 +407,26 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
     };
     ctl.stopcondition = [this]() { return canceled(); };
     ctl.cancelfn = [this]() { throw_if_canceled(); };
-    
+
     po.m_supportdata->create_support_tree(ctl);
-    
+
     if (!po.m_config.supports_enable.getBool()) return;
-    
+
     throw_if_canceled();
-    
+
     // Create the unified mesh
     auto rc = SlicingStatus::RELOAD_SCENE;
-    
+
     // This is to prevent "Done." being displayed during merged_mesh()
     report_status(-1, L("Visualizing supports"));
-    
+
     BOOST_LOG_TRIVIAL(debug) << "Processed support point count "
                              << po.m_supportdata->pts.size();
-    
+
     // Check the mesh for later troubleshooting.
     if(po.support_mesh().empty())
         BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";
-    
+
     report_status(-1, L("Visualizing supports"), rc);
 }
 
@@ -433,15 +434,15 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
     // this step can only go after the support tree has been created
     // and before the supports had been sliced. (or the slicing has to be
     // repeated)
-    
+
     if(po.m_config.pad_enable.getBool()) {
         // Get the distilled pad configuration from the config
         sla::PadConfig pcfg = make_pad_cfg(po.m_config);
-        
+
         ExPolygons bp; // This will store the base plate of the pad.
         double   pad_h             = pcfg.full_height();
         const TriangleMesh &trmesh = po.transformed_mesh();
-        
+
         if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
             // No support (thus no elevation) or zero elevation mode
             // we sometimes call it "builtin pad" is enabled so we will
@@ -451,19 +452,19 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
                                float(po.m_config.layer_height.getFloat()),
                                [this](){ throw_if_canceled(); });
         }
-        
+
         po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
         auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad);
-        
+
         if (!validate_pad(pad_mesh, pcfg))
             throw Slic3r::SlicingError(
                     L("No pad can be generated for this model with the "
                       "current configuration"));
-        
+
     } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) {
         po.m_supportdata->support_tree_ptr->remove_pad();
     }
-    
+
     throw_if_canceled();
     report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE);
 }
@@ -473,25 +474,25 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
 // be part of the slices)
 void SLAPrint::Steps::slice_supports(SLAPrintObject &po) {
     auto& sd = po.m_supportdata;
-    
+
     if(sd) sd->support_slices.clear();
-    
+
     // Don't bother if no supports and no pad is present.
     if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool())
         return;
-    
+
     if(sd && sd->support_tree_ptr) {
         auto heights = reserve_vector<float>(po.m_slice_index.size());
-        
+
         for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level());
 
         sd->support_slices = sd->support_tree_ptr->slice(
             heights, float(po.config().slice_closing_radius.value));
     }
-    
-    for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) 
+
+    for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i)
         po.m_slice_index[i].set_support_slice_idx(po, i);
-    
+
     apply_printer_corrections(po, soSupport);
 
     // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
@@ -506,37 +507,37 @@ using ClipperPolygons = std::vector<ClipperPolygon>;
 static ClipperPolygons polyunion(const ClipperPolygons &subjects)
 {
     ClipperLib::Clipper clipper;
-    
+
     bool closed = true;
-    
+
     for(auto& path : subjects) {
         clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
         clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
     }
-    
+
     auto mode = ClipperLib::pftPositive;
-    
+
     return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
 }
 
 static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips)
 {
     ClipperLib::Clipper clipper;
-    
+
     bool closed = true;
-    
+
     for(auto& path : subjects) {
         clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
         clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
     }
-    
+
     for(auto& path : clips) {
         clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
         clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
     }
-    
+
     auto mode = ClipperLib::pftPositive;
-    
+
     return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
 }
 
@@ -544,28 +545,28 @@ static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPo
 static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
 {
     namespace sl = libnest2d::sl;
-    
+
     if (!record.print_obj()) return {};
-    
+
     ClipperPolygons polygons;
     auto &input_polygons = record.get_slice(o);
     auto &instances = record.print_obj()->instances();
     bool is_lefthanded = record.print_obj()->is_left_handed();
     polygons.reserve(input_polygons.size() * instances.size());
-    
+
     for (const ExPolygon& polygon : input_polygons) {
         if(polygon.contour.empty()) continue;
-        
+
         for (size_t i = 0; i < instances.size(); ++i)
         {
             ClipperPolygon poly;
-            
+
             // We need to reverse if is_lefthanded is true but
             bool needreverse = is_lefthanded;
-            
+
             // should be a move
             poly.Contour.reserve(polygon.contour.size() + 1);
-            
+
             auto& cntr = polygon.contour.points;
             if(needreverse)
                 for(auto it = cntr.rbegin(); it != cntr.rend(); ++it)
@@ -573,12 +574,12 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
             else
                 for(auto& p : cntr)
                     poly.Contour.emplace_back(p.x(), p.y());
-            
+
             for(auto& h : polygon.holes) {
                 poly.Holes.emplace_back();
                 auto& hole = poly.Holes.back();
                 hole.reserve(h.points.size() + 1);
-                
+
                 if(needreverse)
                     for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
                         hole.emplace_back(it->x(), it->y());
@@ -586,42 +587,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
                     for(auto& p : h.points)
                         hole.emplace_back(p.x(), p.y());
             }
-            
+
             if(is_lefthanded) {
                 for(auto& p : poly.Contour) p.X = -p.X;
                 for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X;
             }
-            
+
             sl::rotate(poly, double(instances[i].rotation));
             sl::translate(poly, ClipperPoint{instances[i].shift.x(),
                                              instances[i].shift.y()});
-            
+
             polygons.emplace_back(std::move(poly));
         }
     }
-    
+
     return polygons;
 }
 
 void SLAPrint::Steps::initialize_printer_input()
 {
     auto &printer_input = m_print->m_printer_input;
-    
+
     // clear the rasterizer input
     printer_input.clear();
-    
+
     size_t mx = 0;
     for(SLAPrintObject * o : m_print->m_objects) {
         if(auto m = o->get_slice_index().size() > mx) mx = m;
     }
-    
+
     printer_input.reserve(mx);
-    
+
     auto eps = coord_t(SCALED_EPSILON);
-    
+
     for(SLAPrintObject * o : m_print->m_objects) {
         coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs;
-        
+
         for(const SliceRecord& slicerecord : o->get_slice_index()) {
             if (!slicerecord.is_valid())
                 throw Slic3r::SlicingError(
@@ -630,7 +631,7 @@ void SLAPrint::Steps::initialize_printer_input()
                       "objects printable."));
 
             coord_t lvlid = slicerecord.print_level() - gndlvl;
-            
+
             // Neat trick to round the layer levels to the grid.
             lvlid = eps * (lvlid / eps);
 
@@ -640,8 +641,8 @@ void SLAPrint::Steps::initialize_printer_input()
 
             if(it == printer_input.end() || it->level() != lvlid)
                 it = printer_input.insert(it, PrintLayer(lvlid));
-            
-            
+
+
             it->add(slicerecord);
         }
     }
@@ -650,53 +651,53 @@ void SLAPrint::Steps::initialize_printer_input()
 // Merging the slices from all the print objects into one slice grid and
 // calculating print statistics from the merge result.
 void SLAPrint::Steps::merge_slices_and_eval_stats() {
-    
+
     initialize_printer_input();
-    
+
     auto &print_statistics = m_print->m_print_statistics;
     auto &printer_config   = m_print->m_printer_config;
     auto &material_config  = m_print->m_material_config;
     auto &printer_input    = m_print->m_printer_input;
-    
+
     print_statistics.clear();
-    
+
     // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
     auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); };
-    
+
     const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
     const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0;
     const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0;
-    
+
     const double init_exp_time = material_config.initial_exposure_time.getFloat();
     const double exp_time      = material_config.exposure_time.getFloat();
-    
+
     const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20]
-    
+
     const auto width          = scaled<double>(printer_config.display_width.getFloat());
     const auto height         = scaled<double>(printer_config.display_height.getFloat());
     const double display_area = width*height;
-    
+
     double supports_volume(0.0);
     double models_volume(0.0);
-    
+
     double estim_time(0.0);
     std::vector<double> layers_times;
     layers_times.reserve(printer_input.size());
-    
+
     size_t slow_layers = 0;
     size_t fast_layers = 0;
-    
+
     const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
     double fade_layer_time = init_exp_time;
-    
+
     sla::ccr::SpinningMutex mutex;
     using Lock = std::lock_guard<sla::ccr::SpinningMutex>;
-    
+
     // Going to parallel:
     auto printlayerfn = [this,
             // functions and read only vars
             areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
-            
+
             // write vars
             &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
             &fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt)
@@ -705,87 +706,87 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
 
         // vector of slice record references
         auto& slicerecord_references = layer.slices();
-        
+
         if(slicerecord_references.empty()) return;
-        
+
         // Layer height should match for all object slices for a given level.
         const auto l_height = double(slicerecord_references.front().get().layer_height());
-        
+
         // Calculation of the consumed material
-        
+
         ClipperPolygons model_polygons;
         ClipperPolygons supports_polygons;
-        
+
         size_t c = std::accumulate(layer.slices().begin(),
                                    layer.slices().end(),
                                    size_t(0),
                                    [](size_t a, const SliceRecord &sr) {
             return a + sr.get_slice(soModel).size();
         });
-        
+
         model_polygons.reserve(c);
-        
+
         c = std::accumulate(layer.slices().begin(),
                             layer.slices().end(),
                             size_t(0),
                             [](size_t a, const SliceRecord &sr) {
             return a + sr.get_slice(soModel).size();
         });
-        
+
         supports_polygons.reserve(c);
-        
+
         for(const SliceRecord& record : layer.slices()) {
-            
+
             ClipperPolygons modelslices = get_all_polygons(record, soModel);
             for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
-        
+
             ClipperPolygons supportslices = get_all_polygons(record, soSupport);
             for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
-        
+
         }
-        
+
         model_polygons = polyunion(model_polygons);
         double layer_model_area = 0;
         for (const ClipperPolygon& polygon : model_polygons)
             layer_model_area += areafn(polygon);
-        
+
         if (layer_model_area < 0 || layer_model_area > 0) {
             Lock lck(mutex); models_volume += layer_model_area * l_height;
         }
-        
+
         if(!supports_polygons.empty()) {
             if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons);
             else supports_polygons = polydiff(supports_polygons, model_polygons);
             // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType
         }
-        
+
         double layer_support_area = 0;
         for (const ClipperPolygon& polygon : supports_polygons)
             layer_support_area += areafn(polygon);
-        
+
         if (layer_support_area < 0 || layer_support_area > 0) {
             Lock lck(mutex); supports_volume += layer_support_area * l_height;
         }
-        
+
         // Here we can save the expensively calculated polygons for printing
         ClipperPolygons trslices;
         trslices.reserve(model_polygons.size() + supports_polygons.size());
         for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
         for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
-        
+
         layer.transformed_slices(polyunion(trslices));
-        
+
         // Calculation of the slow and fast layers to the future controlling those values on FW
-        
+
         const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
         const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
-        
+
         { Lock lck(mutex);
             if (is_fast_layer)
                 fast_layers++;
             else
                 slow_layers++;
-            
+
             // Calculation of the printing time
 
             double layer_times = 0.0;
@@ -803,15 +804,15 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
             estim_time += layer_times;
         }
     };
-    
+
     // sequential version for debugging:
     // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i);
     sla::ccr::for_each(size_t(0), printer_input.size(), printlayerfn);
-    
+
     auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR;
     print_statistics.support_used_material = supports_volume * SCALING2;
     print_statistics.objects_used_material = models_volume  * SCALING2;
-    
+
     // Estimated printing time
     // A layers count o the highest object
     if (printer_input.size() == 0)
@@ -820,10 +821,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
         print_statistics.estimated_print_time = estim_time;
         print_statistics.layers_times = layers_times;
     }
-    
+
     print_statistics.fast_layers_count = fast_layers;
     print_statistics.slow_layers_count = slow_layers;
-    
+
     report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
 }
 
@@ -831,23 +832,23 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
 void SLAPrint::Steps::rasterize()
 {
     if(canceled() || !m_print->m_printer) return;
-    
+
     // coefficient to map the rasterization state (0-99) to the allocated
     // portion (slot) of the process state
     double sd = (100 - max_objstatus) / 100.0;
-    
+
     // slot is the portion of 100% that is realted to rasterization
     unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
-    
+
     // pst: previous state
     double pst = current_status();
-    
+
     double increment = (slot * sd) / m_print->m_printer_input.size();
     double dstatus = current_status();
-    
+
     sla::ccr::SpinningMutex slck;
     using Lock = std::lock_guard<sla::ccr::SpinningMutex>;
-    
+
     // procedure to process one height level. This will run in parallel
     auto lvlfn =
         [this, &slck, increment, &dstatus, &pst]
@@ -855,10 +856,10 @@ void SLAPrint::Steps::rasterize()
     {
         PrintLayer& printlayer = m_print->m_printer_input[idx];
         if(canceled()) return;
-        
+
         for (const ClipperLib::Polygon& poly : printlayer.transformed_slices())
             raster.draw(poly);
-        
+
         // Status indication guarded with the spinlock
         {
             Lock lck(slck);
@@ -870,10 +871,10 @@ void SLAPrint::Steps::rasterize()
             }
         }
     };
-    
+
     // last minute escape
     if(canceled()) return;
-    
+
     // Print all the layers in parallel
     m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn);
 }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
index a34c7562e..7f6b10670 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
@@ -200,12 +200,20 @@ void HollowedMesh::on_update()
         if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) {
             size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp;
             if (timestamp > m_old_hollowing_timestamp) {
-                const TriangleMesh& backend_mesh = print_object->get_mesh_to_print();
+                const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice();
                 if (! backend_mesh.empty()) {
                     m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
                     Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse();
                     m_hollowed_mesh_transformed->transform(trafo_inv);
                     m_old_hollowing_timestamp = timestamp;
+
+                    const TriangleMesh &interior = print_object->hollowed_interior_mesh();
+                    if (!interior.empty()) {
+                        m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(interior);
+                        m_hollowed_interior_transformed->repaired = false;
+                        m_hollowed_interior_transformed->repair(true);
+                        m_hollowed_interior_transformed->transform(trafo_inv);
+                    }
                 }
                 else
                     m_hollowed_mesh_transformed.reset(nullptr);
@@ -230,6 +238,10 @@ const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
     return m_hollowed_mesh_transformed.get();
 }
 
+const TriangleMesh* HollowedMesh::get_hollowed_interior() const
+{
+    return m_hollowed_interior_transformed.get();
+}
 
 
 
@@ -306,6 +318,10 @@ void ObjectClipper::on_update()
             m_clippers.back()->set_mesh(*mesh);
         }
         m_old_meshes = meshes;
+
+        if (has_hollowed)
+            m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior());
+
         m_active_inst_bb_radius =
             mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
         //if (has_hollowed && m_clp_ratio != 0.)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
index 61c273297..ace256748 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
@@ -199,6 +199,7 @@ public:
 #endif // NDEBUG
 
     const TriangleMesh* get_hollowed_mesh() const;
+    const TriangleMesh* get_hollowed_interior() const;
 
 protected:
     void on_update() override;
@@ -206,6 +207,7 @@ protected:
 
 private:
     std::unique_ptr<TriangleMesh> m_hollowed_mesh_transformed;
+    std::unique_ptr<TriangleMesh> m_hollowed_interior_transformed;
     size_t m_old_hollowing_timestamp = 0;
     int m_print_object_idx = -1;
     int m_print_objects_count = 0;
diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp
index ee0abe76f..f9ccfd0d6 100644
--- a/src/slic3r/GUI/MeshUtils.cpp
+++ b/src/slic3r/GUI/MeshUtils.cpp
@@ -2,6 +2,7 @@
 
 #include "libslic3r/Tesselate.hpp"
 #include "libslic3r/TriangleMesh.hpp"
+#include "libslic3r/ClipperUtils.hpp"
 
 #include "slic3r/GUI/Camera.hpp"
 
@@ -31,6 +32,15 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh)
     }
 }
 
+void MeshClipper::set_negative_mesh(const TriangleMesh& mesh)
+{
+    if (m_negative_mesh != &mesh) {
+        m_negative_mesh = &mesh;
+        m_triangles_valid = false;
+        m_triangles2d.resize(0);
+    }
+}
+
 
 
 void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
@@ -74,6 +84,15 @@ void MeshClipper::recalculate_triangles()
     std::vector<ExPolygons> list_of_expolys;
     m_tms->set_up_direction(up.cast<float>());
     m_tms->slice(std::vector<float>{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){});
+
+    if (m_negative_mesh && !m_negative_mesh->empty()) {
+        TriangleMeshSlicer negative_tms{m_negative_mesh};
+        negative_tms.set_up_direction(up.cast<float>());
+
+        std::vector<ExPolygons> neg_polys;
+        negative_tms.slice(std::vector<float>{height_mesh}, SlicingMode::Regular, 0.f, &neg_polys, [](){});
+        list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front());
+    }
     m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.);
 
     // Rotate the cut into world coords:
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
index 60dcb30c8..09caf199b 100644
--- a/src/slic3r/GUI/MeshUtils.hpp
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -78,6 +78,8 @@ public:
     // must make sure that it stays valid.
     void set_mesh(const TriangleMesh& mesh);
 
+    void set_negative_mesh(const TriangleMesh &mesh);
+
     // Inform the MeshClipper about the transformation that transforms the mesh
     // into world coordinates.
     void set_transformation(const Geometry::Transformation& trafo);
@@ -91,6 +93,7 @@ private:
 
     Geometry::Transformation m_trafo;
     const TriangleMesh* m_mesh = nullptr;
+    const TriangleMesh* m_negative_mesh = nullptr;
     ClippingPlane m_plane;
     std::vector<Vec2f> m_triangles2d;
     GLIndexedVertexArray m_vertex_array;