From 4cca6f8724321e1247fd7c5ba92bada7a561cc51 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 14 Aug 2019 15:44:32 +0200
Subject: [PATCH 01/10] Allowed the skirt to intersect brim
 (https://github.com/prusa3d/PrusaSlicer/issues/724) Brim lines are split at
 the intersection so there is no overextrusion.

---
 src/libslic3r/GCode.cpp | 10 +++----
 src/libslic3r/Print.cpp | 66 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 67 insertions(+), 9 deletions(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index b691203c9..96aa60c25 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1752,8 +1752,9 @@ void GCode::process_layer(
         if (! m_brim_done) {
             this->set_origin(0., 0.);
             m_avoid_crossing_perimeters.use_external_mp = true;
-            for (const ExtrusionEntity *ee : print.brim().entities)
-                gcode += this->extrude_loop(*dynamic_cast<const ExtrusionLoop*>(ee), "brim", m_config.support_material_speed.value);
+            for (const ExtrusionEntity *ee : print.brim().entities) {
+                gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value);
+            }
             m_brim_done = true;
             m_avoid_crossing_perimeters.use_external_mp = false;
             // Allow a straight travel move to the first object point.
@@ -2419,10 +2420,9 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des
         return this->extrude_multi_path(*multipath, description, speed);
     else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
         return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid);
-    else {
+    else
         throw std::invalid_argument("Invalid argument supplied to extrude()");
-        return "";
-    }
+    return "";
 }
 
 std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed)
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index e376f54c3..ddd99a3ee 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1603,9 +1603,7 @@ void Print::_make_skirt()
 
     // Initial offset of the brim inner edge from the object (possible with a support & raft).
     // The skirt will touch the brim if the brim is extruded.
-    Flow   brim_flow = this->brim_flow();
-    double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
-    auto   distance = float(scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.));
+    auto   distance = float(scale_(m_config.skirt_distance.value) - spacing/2.);
     // Draw outlines from outside to inside.
     // Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
     std::vector<coordf_t> extruded_length(extruders.size(), 0.);
@@ -1687,11 +1685,71 @@ void Print::_make_brim()
         }
         polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
     }
-
     loops = union_pt_chained(loops, false);
     // The function above produces ordering well suited for concentric infill (from outside to inside).
     // For Brim, the ordering should be reversed (from inside to outside).
     std::reverse(loops.begin(), loops.end());
+
+    // If there is a possibility that brim intersects skirt, go through loops and split those extrusions
+    // The result is either the original Polygon or a list of Polylines
+    if (! m_skirt.empty() && m_config.skirt_distance.value < m_config.brim_width)
+    {
+        // Find the bounding polygons of the skirt
+        const Polygons skirt_inners = offset(dynamic_cast<ExtrusionLoop*>(m_skirt.entities.back())->polygon(),
+                                              -float(scale_(this->skirt_flow().spacing()))/2.f,
+                                              ClipperLib::jtRound,
+                                              float(scale_(0.1)));
+        const Polygons skirt_outers = offset(dynamic_cast<ExtrusionLoop*>(m_skirt.entities.front())->polygon(),
+                                              float(scale_(this->skirt_flow().spacing()))/2.f,
+                                              ClipperLib::jtRound,
+                                              float(scale_(0.1)));
+
+        const Polygon& skirt_inner = skirt_inners.front();
+        const Polygon& skirt_outer = skirt_outers.front();
+
+        for (size_t i=0; i<loops.size(); ++i) {
+            Polylines lines;
+            lines.push_back(Polyline());
+            bool del = false;
+            const Polygon& poly = loops[i];
+            // Check all points of the polygon, consider the first to also be at the end so the loop is closed
+            for (int pt_idx=0; pt_idx <= (int)poly.points.size(); ++pt_idx) {
+                const Point* pt = (pt_idx == (int)poly.points.size() ? &poly.points[0] : &poly.points[pt_idx]);
+                const Point* last_pt = (pt_idx == 0 ? nullptr : &poly.points[pt_idx-1]);
+                bool valid_point = skirt_inner.contains(*pt) || ! skirt_outer.contains(*pt);
+                Points intersections(2); // inner and outer intersection
+
+                if (pt_idx > 0) {
+                    Line line(*last_pt, *pt);
+                    bool inner = skirt_inner.first_intersection(line, &intersections[0]);
+                    bool outer = skirt_outer.first_intersection(line, &intersections[1]);
+                    del = del || inner || outer;
+                    if (inner != outer) {// there is exactly one intersection
+                        lines.back().append(inner ? intersections[0] : intersections[1]);
+                    }
+                    if (inner && outer) {
+                        int nearest_idx = pt->nearest_point_index(intersections);
+                        lines.back().append(intersections[! nearest_idx]);
+                        lines.push_back(Polyline());
+                        lines.back().append(intersections[nearest_idx]);
+                    }
+                }
+                if (valid_point)
+                    lines.back().append(*pt);
+                else {
+                    del = true;
+                    lines.push_back(Polyline());
+                }
+            }
+            // If we found a single intersection, we should erase the respective ExtrusionLoop
+            // and append the polylines that we created.
+            if (del) {
+                loops.erase(loops.begin() + (i--));
+                extrusion_entities_append_paths(m_brim.entities, std::move(lines), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
+            }
+        }
+    }
+
     extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
 }
 

From 0989cb82963aa0f1f2dad77b5f494d9c5718c48f Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 11 Sep 2019 11:37:48 +0200
Subject: [PATCH 02/10] Refactoring of PerimeterGenerator: header interface was
 reduced, compiler warnings removed.

---
 src/libslic3r/PerimeterGenerator.cpp | 487 ++++++++++++++-------------
 src/libslic3r/PerimeterGenerator.hpp |  44 +--
 2 files changed, 267 insertions(+), 264 deletions(-)

diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index 74df07935..793657817 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -6,21 +6,246 @@
 
 namespace Slic3r {
 
+static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance)
+{
+    ExtrusionPaths paths;
+    ExtrusionPath path(role);
+    ThickLines lines = thick_polyline.thicklines();
+    
+    for (int i = 0; i < (int)lines.size(); ++i) {
+        const ThickLine& line = lines[i];
+        
+        const coordf_t line_len = line.length();
+        if (line_len < SCALED_EPSILON) continue;
+        
+        double thickness_delta = fabs(line.a_width - line.b_width);
+        if (thickness_delta > tolerance) {
+            const unsigned int segments = (unsigned int)ceil(thickness_delta / tolerance);
+            const coordf_t seg_len = line_len / segments;
+            Points pp;
+            std::vector<coordf_t> width;
+            {
+                pp.push_back(line.a);
+                width.push_back(line.a_width);
+                for (size_t j = 1; j < segments; ++j) {
+                    pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
+                    
+                    coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
+                    width.push_back(w);
+                    width.push_back(w);
+                }
+                pp.push_back(line.b);
+                width.push_back(line.b_width);
+                
+                assert(pp.size() == segments + 1u);
+                assert(width.size() == segments*2);
+            }
+            
+            // delete this line and insert new ones
+            lines.erase(lines.begin() + i);
+            for (size_t j = 0; j < segments; ++j) {
+                ThickLine new_line(pp[j], pp[j+1]);
+                new_line.a_width = width[2*j];
+                new_line.b_width = width[2*j+1];
+                lines.insert(lines.begin() + i + j, new_line);
+            }
+            
+            -- i;
+            continue;
+        }
+        
+        const double w = fmax(line.a_width, line.b_width);
+        if (path.polyline.points.empty()) {
+            path.polyline.append(line.a);
+            path.polyline.append(line.b);
+            // Convert from spacing to extrusion width based on the extrusion model
+            // of a square extrusion ended with semi circles.
+            flow.width = unscale<float>(w) + flow.height * float(1. - 0.25 * PI);
+            #ifdef SLIC3R_DEBUG
+            printf("  filling %f gap\n", flow.width);
+            #endif
+            path.mm3_per_mm  = flow.mm3_per_mm();
+            path.width       = flow.width;
+            path.height      = flow.height;
+        } else {
+            thickness_delta = fabs(scale_(flow.width) - w);
+            if (thickness_delta <= tolerance) {
+                // the width difference between this line and the current flow width is 
+                // within the accepted tolerance
+                path.polyline.append(line.b);
+            } else {
+                // we need to initialize a new line
+                paths.emplace_back(std::move(path));
+                path = ExtrusionPath(role);
+                -- i;
+            }
+        }
+    }
+    if (path.polyline.is_valid())
+        paths.emplace_back(std::move(path));
+    return paths;
+}
+
+static ExtrusionEntityCollection variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow)
+{
+	// This value determines granularity of adaptive width, as G-code does not allow
+	// variable extrusion within a single move; this value shall only affect the amount
+	// of segments, and any pruning shall be performed before we apply this tolerance.
+	ExtrusionEntityCollection coll;
+	const float tolerance = float(scale_(0.05));
+	for (const ThickPolyline &p : polylines) {
+		ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance);
+		// Append paths to collection.
+		if (! paths.empty()) {
+			if (paths.front().first_point() == paths.back().last_point())
+				coll.append(ExtrusionLoop(std::move(paths)));
+			else
+				coll.append(std::move(paths));
+		}
+	}
+	return coll;
+}
+
+// Hierarchy of perimeters.
+class PerimeterGeneratorLoop {
+public:
+    // Polygon of this contour.
+    Polygon polygon;
+    // Is it a contour or a hole?
+    // Contours are CCW oriented, holes are CW oriented.
+    bool is_contour;
+    // Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole.
+    unsigned short depth;
+    // Children contour, may be both CCW and CW oriented (outer contours or holes).
+    std::vector<PerimeterGeneratorLoop> children;
+    
+    PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour) : 
+        polygon(polygon), is_contour(is_contour), depth(depth) {}
+    // External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
+    bool is_external() const { return this->depth == 0; }
+    // An island, which may have holes, but it does not have another internal island.
+    bool is_internal_contour() const;
+};
+
+typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops;
+
+static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
+{
+    // loops is an arrayref of ::Loop objects
+    // turn each one into an ExtrusionLoop object
+    ExtrusionEntityCollection coll;
+    for (const PerimeterGeneratorLoop &loop : loops) {
+        bool is_external = loop.is_external();
+        
+        ExtrusionRole role;
+        ExtrusionLoopRole loop_role;
+        role = is_external ? erExternalPerimeter : erPerimeter;
+        if (loop.is_internal_contour()) {
+            // Note that we set loop role to ContourInternalPerimeter
+            // also when loop is both internal and external (i.e.
+            // there's only one contour loop).
+            loop_role = elrContourInternalPerimeter;
+        } else {
+            loop_role = elrDefault;
+        }
+        
+        // detect overhanging/bridging perimeters
+        ExtrusionPaths paths;
+        if (perimeter_generator.config->overhangs && perimeter_generator.layer_id > 0
+            && !(perimeter_generator.object_config->support_material && perimeter_generator.object_config->support_material_contact_distance.value == 0)) {
+            // get non-overhang paths by intersecting this loop with the grown lower slices
+            extrusion_paths_append(
+                paths,
+                intersection_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
+                role,
+                is_external ? perimeter_generator.ext_mm3_per_mm()          : perimeter_generator.mm3_per_mm(),
+                is_external ? perimeter_generator.ext_perimeter_flow.width  : perimeter_generator.perimeter_flow.width,
+                (float)perimeter_generator.layer_height);
+            
+            // get overhang paths by checking what parts of this loop fall 
+            // outside the grown lower slices (thus where the distance between
+            // the loop centerline and original lower slices is >= half nozzle diameter
+            extrusion_paths_append(
+                paths,
+                diff_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
+                erOverhangPerimeter,
+                perimeter_generator.mm3_per_mm_overhang(),
+                perimeter_generator.overhang_flow.width,
+                perimeter_generator.overhang_flow.height);
+            
+            // reapply the nearest point search for starting point
+            // We allow polyline reversal because Clipper may have randomly
+            // reversed polylines during clipping.
+            paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path();
+        } else {
+            ExtrusionPath path(role);
+            path.polyline   = loop.polygon.split_at_first_point();
+            path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm()          : perimeter_generator.mm3_per_mm();
+            path.width      = is_external ? perimeter_generator.ext_perimeter_flow.width  : perimeter_generator.perimeter_flow.width;
+            path.height     = (float)perimeter_generator.layer_height;
+            paths.push_back(path);
+        }
+        
+        coll.append(ExtrusionLoop(paths, loop_role));
+    }
+    
+    // append thin walls to the nearest-neighbor search (only for first iteration)
+    if (!thin_walls.empty()) {
+        ExtrusionEntityCollection tw = variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow);
+        coll.append(tw.entities);
+        thin_walls.clear();
+    }
+    
+    // sort entities into a new collection using a nearest-neighbor search,
+    // preserving the original indices which are useful for detecting thin walls
+    ExtrusionEntityCollection sorted_coll;
+    coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices);
+    
+    // traverse children and build the final collection
+    ExtrusionEntityCollection entities;
+    for (std::vector<size_t>::const_iterator idx = sorted_coll.orig_indices.begin();
+        idx != sorted_coll.orig_indices.end();
+        ++idx) {
+        
+        if (*idx >= loops.size()) {
+            // this is a thin wall
+            // let's get it from the sorted collection as it might have been reversed
+            size_t i = idx - sorted_coll.orig_indices.begin();
+            entities.append(*sorted_coll.entities[i]);
+        } else {
+            const PerimeterGeneratorLoop &loop = loops[*idx];
+            ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]);
+            
+            ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
+            if (loop.is_contour) {
+                eloop.make_counter_clockwise();
+                entities.append(children.entities);
+                entities.append(eloop);
+            } else {
+                eloop.make_clockwise();
+                entities.append(eloop);
+                entities.append(children.entities);
+            }
+        }
+    }
+    return entities;
+}
+
 void PerimeterGenerator::process()
 {
     // other perimeters
-    this->_mm3_per_mm               = this->perimeter_flow.mm3_per_mm();
+    m_mm3_per_mm               		= this->perimeter_flow.mm3_per_mm();
     coord_t perimeter_width         = this->perimeter_flow.scaled_width();
     coord_t perimeter_spacing       = this->perimeter_flow.scaled_spacing();
     
     // external perimeters
-    this->_ext_mm3_per_mm           = this->ext_perimeter_flow.mm3_per_mm();
+    m_ext_mm3_per_mm           		= this->ext_perimeter_flow.mm3_per_mm();
     coord_t ext_perimeter_width     = this->ext_perimeter_flow.scaled_width();
     coord_t ext_perimeter_spacing   = this->ext_perimeter_flow.scaled_spacing();
     coord_t ext_perimeter_spacing2  = this->ext_perimeter_flow.scaled_spacing(this->perimeter_flow);
     
     // overhang perimeters
-    this->_mm3_per_mm_overhang      = this->overhang_flow.mm3_per_mm();
+    m_mm3_per_mm_overhang      		= this->overhang_flow.mm3_per_mm();
     
     // solid infill
     coord_t solid_infill_spacing    = this->solid_infill_flow.scaled_spacing();
@@ -35,8 +260,8 @@ void PerimeterGenerator::process()
     // which is the spacing between external and internal, which is not correct
     // and would make the collapsing (thus the details resolution) dependent on 
     // internal flow which is unrelated.
-    coord_t min_spacing         = perimeter_spacing      * (1 - INSET_OVERLAP_TOLERANCE);
-    coord_t ext_min_spacing     = ext_perimeter_spacing  * (1 - INSET_OVERLAP_TOLERANCE);
+    coord_t min_spacing         = coord_t(perimeter_spacing      * (1 - INSET_OVERLAP_TOLERANCE));
+    coord_t ext_min_spacing     = coord_t(ext_perimeter_spacing  * (1 - INSET_OVERLAP_TOLERANCE));
     bool    has_gap_fill 		= this->config->gap_fill_speed.value > 0;
 
     // prepare grown lower layer slices for overhang detection
@@ -45,7 +270,7 @@ void PerimeterGenerator::process()
         // lower layer, so we take lower slices and offset them by half the nozzle diameter used 
         // in the current layer
         double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1);
-        this->_lower_slices_p = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2)));
+        m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2)));
     }
     
     // we need to process each island separately because we might have different
@@ -70,20 +295,20 @@ void PerimeterGenerator::process()
                     offsets = this->config->thin_walls ? 
                         offset2_ex(
                             last,
-                            -(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1),
-                            +(ext_min_spacing / 2 - 1)) :
-                        offset_ex(last, - ext_perimeter_width / 2);
+                            - float(ext_perimeter_width / 2. + ext_min_spacing / 2. - 1),
+                            + float(ext_min_spacing / 2. - 1)) :
+                        offset_ex(last, - float(ext_perimeter_width / 2.));
                     // look for thin walls
                     if (this->config->thin_walls) {
                         // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
                         // (actually, something larger than that still may exist due to mitering or other causes)
-                        coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3);
+                        coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter / 3));
                         ExPolygons expp = offset2_ex(
                             // medial axis requires non-overlapping geometry
                             diff_ex(to_polygons(last),
-                                    offset(offsets, ext_perimeter_width / 2),
+                                    offset(offsets, float(ext_perimeter_width / 2.)),
                                     true),
-                            - min_width / 2, min_width / 2);
+                            - float(min_width / 2.), float(min_width / 2.));
                         // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
                         for (ExPolygon &ex : expp)
                             ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls);
@@ -100,19 +325,19 @@ void PerimeterGenerator::process()
                         // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
                         // the original.
                         offset2_ex(last,
-                                - (distance + min_spacing / 2 - 1),
-                                min_spacing / 2 - 1) :
+                                - float(distance + min_spacing / 2. - 1.),
+                                float(min_spacing / 2. - 1.)) :
                         // If "detect thin walls" is not enabled, this paths will be entered, which 
                         // leads to overflows, as in prusa3d/Slic3r GH #32
-                        offset_ex(last, - distance);
+                        offset_ex(last, - float(distance));
                     // look for gaps
                     if (has_gap_fill)
                         // not using safety offset here would "detect" very narrow gaps
                         // (but still long enough to escape the area threshold) that gap fill
                         // won't be able to fill but we'd still remove from infill area
                         append(gaps, diff_ex(
-                            offset(last,    -0.5 * distance),
-                            offset(offsets,  0.5 * distance + 10)));  // safety offset
+                            offset(last,    - float(0.5 * distance)),
+                            offset(offsets,   float(0.5 * distance + 10))));  // safety offset
                 }
                 if (offsets.empty()) {
                     // Store the number of loops actually generated.
@@ -125,6 +350,11 @@ void PerimeterGenerator::process()
                     break;
                 }
                 for (const ExPolygon &expolygon : offsets) {
+	                // Outer contour may overlap with an inner contour,
+	                // inner contour may overlap with another inner contour,
+	                // outer contour may overlap with itself.
+	                //FIXME evaluate the overlaps, annotate each point with an overlap depth,
+	                // compensate for the depth of intersection.
                     contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true));
                     if (! expolygon.holes.empty()) {
                         holes[i].reserve(holes[i].size() + expolygon.holes.size());
@@ -195,7 +425,7 @@ void PerimeterGenerator::process()
                 }
             }
             // at this point, all loops should be in contours[0]
-            ExtrusionEntityCollection entities = this->_traverse_loops(contours.front(), thin_walls);
+            ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls);
             // if brim will be printed, reverse the order of perimeters so that
             // we continue inwards after having finished the brim
             // TODO: add test for perimeter order
@@ -214,15 +444,14 @@ void PerimeterGenerator::process()
             double max = 2. * perimeter_spacing;
             ExPolygons gaps_ex = diff_ex(
                 //FIXME offset2 would be enough and cheaper.
-                offset2_ex(gaps, -min/2, +min/2),
-                offset2_ex(gaps, -max/2, +max/2),
+                offset2_ex(gaps, - float(min / 2.), float(min / 2.)),
+                offset2_ex(gaps, - float(max / 2.), float(max / 2.)),
                 true);
             ThickPolylines polylines;
             for (const ExPolygon &ex : gaps_ex)
                 ex.medial_axis(max, min, &polylines);
             if (! polylines.empty()) {
-                ExtrusionEntityCollection gap_fill = this->_variable_width(polylines, 
-                    erGapFill, this->solid_infill_flow);
+                ExtrusionEntityCollection gap_fill = variable_width(polylines, erGapFill, this->solid_infill_flow);
                 this->gap_fill->append(gap_fill.entities);
                 /*  Make sure we don't infill narrow parts that are already gap-filled
                     (we only consider this surface's gaps to reduce the diff() complexity).
@@ -249,229 +478,23 @@ void PerimeterGenerator::process()
                 perimeter_spacing / 2;
         // only apply infill overlap if we actually have one perimeter
         if (inset > 0)
-            inset -= scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2)));
+            inset -= coord_t(scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
         // simplify infill contours according to resolution
         Polygons pp;
         for (ExPolygon &ex : last)
             ex.simplify_p(SCALED_RESOLUTION, &pp);
         // collapse too narrow infill areas
-        coord_t min_perimeter_infill_spacing = solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE);
+        coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
         // append infill areas to fill_surfaces
         this->fill_surfaces->append(
             offset2_ex(
                 union_ex(pp),
-                - inset - min_perimeter_infill_spacing / 2,
-                min_perimeter_infill_spacing / 2),
+                float(- inset - min_perimeter_infill_spacing / 2.),
+                float(min_perimeter_infill_spacing / 2.)),
             stInternal);
     } // for each island
 }
 
-ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
-    const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const
-{
-    // loops is an arrayref of ::Loop objects
-    // turn each one into an ExtrusionLoop object
-    ExtrusionEntityCollection coll;
-    for (PerimeterGeneratorLoops::const_iterator loop = loops.begin();
-        loop != loops.end(); ++loop) {
-        bool is_external = loop->is_external();
-        
-        ExtrusionRole role;
-        ExtrusionLoopRole loop_role;
-        role = is_external ? erExternalPerimeter : erPerimeter;
-        if (loop->is_internal_contour()) {
-            // Note that we set loop role to ContourInternalPerimeter
-            // also when loop is both internal and external (i.e.
-            // there's only one contour loop).
-            loop_role = elrContourInternalPerimeter;
-        } else {
-            loop_role = elrDefault;
-        }
-        
-        // detect overhanging/bridging perimeters
-        ExtrusionPaths paths;
-        if (this->config->overhangs && this->layer_id > 0
-            && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
-            // get non-overhang paths by intersecting this loop with the grown lower slices
-            extrusion_paths_append(
-                paths,
-                intersection_pl(loop->polygon, this->_lower_slices_p),
-                role,
-                is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm,
-                is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width,
-                this->layer_height);
-            
-            // get overhang paths by checking what parts of this loop fall 
-            // outside the grown lower slices (thus where the distance between
-            // the loop centerline and original lower slices is >= half nozzle diameter
-            extrusion_paths_append(
-                paths,
-                diff_pl(loop->polygon, this->_lower_slices_p),
-                erOverhangPerimeter,
-                this->_mm3_per_mm_overhang,
-                this->overhang_flow.width,
-                this->overhang_flow.height);
-            
-            // reapply the nearest point search for starting point
-            // We allow polyline reversal because Clipper may have randomly
-            // reversed polylines during clipping.
-            paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path();
-        } else {
-            ExtrusionPath path(role);
-            path.polyline   = loop->polygon.split_at_first_point();
-            path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm;
-            path.width      = is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width;
-            path.height     = this->layer_height;
-            paths.push_back(path);
-        }
-        
-        coll.append(ExtrusionLoop(paths, loop_role));
-    }
-    
-    // append thin walls to the nearest-neighbor search (only for first iteration)
-    if (!thin_walls.empty()) {
-        ExtrusionEntityCollection tw = this->_variable_width
-            (thin_walls, erExternalPerimeter, this->ext_perimeter_flow);
-        
-        coll.append(tw.entities);
-        thin_walls.clear();
-    }
-    
-    // sort entities into a new collection using a nearest-neighbor search,
-    // preserving the original indices which are useful for detecting thin walls
-    ExtrusionEntityCollection sorted_coll;
-    coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices);
-    
-    // traverse children and build the final collection
-    ExtrusionEntityCollection entities;
-    for (std::vector<size_t>::const_iterator idx = sorted_coll.orig_indices.begin();
-        idx != sorted_coll.orig_indices.end();
-        ++idx) {
-        
-        if (*idx >= loops.size()) {
-            // this is a thin wall
-            // let's get it from the sorted collection as it might have been reversed
-            size_t i = idx - sorted_coll.orig_indices.begin();
-            entities.append(*sorted_coll.entities[i]);
-        } else {
-            const PerimeterGeneratorLoop &loop = loops[*idx];
-            ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]);
-            
-            ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls);
-            if (loop.is_contour) {
-                eloop.make_counter_clockwise();
-                entities.append(children.entities);
-                entities.append(eloop);
-            } else {
-                eloop.make_clockwise();
-                entities.append(eloop);
-                entities.append(children.entities);
-            }
-        }
-    }
-    return entities;
-}
-
-static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance)
-{
-    ExtrusionPaths paths;
-    ExtrusionPath path(role);
-    ThickLines lines = thick_polyline.thicklines();
-    
-    for (int i = 0; i < (int)lines.size(); ++i) {
-        const ThickLine& line = lines[i];
-        
-        const coordf_t line_len = line.length();
-        if (line_len < SCALED_EPSILON) continue;
-        
-        double thickness_delta = fabs(line.a_width - line.b_width);
-        if (thickness_delta > tolerance) {
-            const unsigned short segments = ceil(thickness_delta / tolerance);
-            const coordf_t seg_len = line_len / segments;
-            Points pp;
-            std::vector<coordf_t> width;
-            {
-                pp.push_back(line.a);
-                width.push_back(line.a_width);
-                for (size_t j = 1; j < segments; ++j) {
-                    pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
-                    
-                    coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
-                    width.push_back(w);
-                    width.push_back(w);
-                }
-                pp.push_back(line.b);
-                width.push_back(line.b_width);
-                
-                assert(pp.size() == segments + 1u);
-                assert(width.size() == segments*2);
-            }
-            
-            // delete this line and insert new ones
-            lines.erase(lines.begin() + i);
-            for (size_t j = 0; j < segments; ++j) {
-                ThickLine new_line(pp[j], pp[j+1]);
-                new_line.a_width = width[2*j];
-                new_line.b_width = width[2*j+1];
-                lines.insert(lines.begin() + i + j, new_line);
-            }
-            
-            -- i;
-            continue;
-        }
-        
-        const double w = fmax(line.a_width, line.b_width);
-        if (path.polyline.points.empty()) {
-            path.polyline.append(line.a);
-            path.polyline.append(line.b);
-            // Convert from spacing to extrusion width based on the extrusion model
-            // of a square extrusion ended with semi circles.
-            flow.width = unscale<float>(w) + flow.height * (1. - 0.25 * PI);
-            #ifdef SLIC3R_DEBUG
-            printf("  filling %f gap\n", flow.width);
-            #endif
-            path.mm3_per_mm  = flow.mm3_per_mm();
-            path.width       = flow.width;
-            path.height      = flow.height;
-        } else {
-            thickness_delta = fabs(scale_(flow.width) - w);
-            if (thickness_delta <= tolerance) {
-                // the width difference between this line and the current flow width is 
-                // within the accepted tolerance
-                path.polyline.append(line.b);
-            } else {
-                // we need to initialize a new line
-                paths.emplace_back(std::move(path));
-                path = ExtrusionPath(role);
-                -- i;
-            }
-        }
-    }
-    if (path.polyline.is_valid())
-        paths.emplace_back(std::move(path));
-    return paths;
-}
-
-ExtrusionEntityCollection PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const
-{
-    // This value determines granularity of adaptive width, as G-code does not allow
-    // variable extrusion within a single move; this value shall only affect the amount
-    // of segments, and any pruning shall be performed before we apply this tolerance.
-    ExtrusionEntityCollection coll;
-    const double tolerance = scale_(0.05);
-    for (const ThickPolyline &p : polylines) {
-        ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance);
-        // Append paths to collection.
-        if (! paths.empty()) {
-            if (paths.front().first_point() == paths.back().last_point())
-                coll.append(ExtrusionLoop(std::move(paths)));
-            else
-                coll.append(std::move(paths));
-        }
-    }
-    return coll;
-}
-
 bool PerimeterGeneratorLoop::is_internal_contour() const
 {
     // An internal contour is a contour containing no other contours
diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp
index c01abbb15..8cd71e697 100644
--- a/src/libslic3r/PerimeterGenerator.hpp
+++ b/src/libslic3r/PerimeterGenerator.hpp
@@ -11,29 +11,6 @@
 
 namespace Slic3r {
 
-// Hierarchy of perimeters.
-class PerimeterGeneratorLoop {
-public:
-    // Polygon of this contour.
-    Polygon polygon;
-    // Is it a contour or a hole?
-    // Contours are CCW oriented, holes are CW oriented.
-    bool is_contour;
-    // Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole.
-    unsigned short depth;
-    // Children contour, may be both CCW and CW oriented (outer contours or holes).
-    std::vector<PerimeterGeneratorLoop> children;
-    
-    PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour) : 
-        polygon(polygon), is_contour(is_contour), depth(depth) {}
-    // External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
-    bool is_external() const { return this->depth == 0; }
-    // An island, which may have holes, but it does not have another internal island.
-    bool is_internal_contour() const;
-};
-
-typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops;
-
 class PerimeterGenerator {
 public:
     // Inputs:
@@ -73,18 +50,21 @@ public:
             overhang_flow(flow), solid_infill_flow(flow),
             config(config), object_config(object_config), print_config(print_config),
             loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
-            _ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1)
+            m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1)
         {}
-    void process();
+
+    void        process();
+
+    double      ext_mm3_per_mm()        const { return m_ext_mm3_per_mm; }
+    double      mm3_per_mm()            const { return m_mm3_per_mm; }
+    double      mm3_per_mm_overhang()   const { return m_mm3_per_mm_overhang; }
+    Polygons    lower_slices_polygons() const { return m_lower_slices_polygons; }
 
 private:
-    double      _ext_mm3_per_mm;
-    double      _mm3_per_mm;
-    double      _mm3_per_mm_overhang;
-    Polygons    _lower_slices_p;
-    
-    ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const;
-    ExtrusionEntityCollection _variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
+    double      m_ext_mm3_per_mm;
+    double      m_mm3_per_mm;
+    double      m_mm3_per_mm_overhang;
+    Polygons    m_lower_slices_polygons;
 };
 
 }

From b3f27b8fb9c7f8491aff0c715587bfbe08de76c2 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 11 Sep 2019 11:38:17 +0200
Subject: [PATCH 03/10] Fixed typo in comments

---
 src/libslic3r/ExtrusionEntity.hpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index 1d2465ae4..2a66489fe 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -111,13 +111,13 @@ public:
     double mm3_per_mm;
     // Width of the extrusion, used for visualization purposes.
     float width;
-    // Height of the extrusion, used for visualization purposed.
+    // Height of the extrusion, used for visualization purposes.
     float height;
-    // Feedrate of the extrusion, used for visualization purposed.
+    // Feedrate of the extrusion, used for visualization purposes.
     float feedrate;
-    // Id of the extruder, used for visualization purposed.
+    // Id of the extruder, used for visualization purposes.
     unsigned int extruder_id;
-    // Id of the color, used for visualization purposed in the color printing case.
+    // Id of the color, used for visualization purposes in the color printing case.
     unsigned int cp_color_id;
 
     ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}

From 15c8b579b2b17f18825cb49c542bd7f4b42d90b2 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 11 Sep 2019 13:25:50 +0200
Subject: [PATCH 04/10] Refactoring of ExtrusionEntity /
 ExtrusionEntityCollection: Iterator loops replaced with C++11 loops. Fixed
 clone() methods to return an ExtrusionEntity*. PerimeterGenerator now uses
 move semantics on ExtrusionEntity a little bit more.

---
 src/libslic3r/ExtrusionEntity.cpp           | 123 ++++++++------------
 src/libslic3r/ExtrusionEntity.hpp           |  18 ++-
 src/libslic3r/ExtrusionEntityCollection.cpp | 114 ++++++++----------
 src/libslic3r/ExtrusionEntityCollection.hpp |  14 ++-
 src/libslic3r/PerimeterGenerator.cpp        |  34 +++---
 5 files changed, 132 insertions(+), 171 deletions(-)

diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp
index 035fe3242..7f57b78af 100644
--- a/src/libslic3r/ExtrusionEntity.cpp
+++ b/src/libslic3r/ExtrusionEntity.cpp
@@ -12,44 +12,35 @@
 
 namespace Slic3r {
     
-void
-ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
+void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
 {
     this->_inflate_collection(intersection_pl(this->polyline, collection), retval);
 }
 
-void
-ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
+void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
 {
     this->_inflate_collection(diff_pl(this->polyline, collection), retval);
 }
 
-void
-ExtrusionPath::clip_end(double distance)
+void ExtrusionPath::clip_end(double distance)
 {
     this->polyline.clip_end(distance);
 }
 
-void
-ExtrusionPath::simplify(double tolerance)
+void ExtrusionPath::simplify(double tolerance)
 {
     this->polyline.simplify(tolerance);
 }
 
-double
-ExtrusionPath::length() const
+double ExtrusionPath::length() const
 {
     return this->polyline.length();
 }
 
-void
-ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
+void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
 {
-    for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) {
-        ExtrusionPath* path = this->clone();
-        path->polyline = *it;
-        collection->entities.push_back(path);
-    }
+    for (const Polyline &polyline : polylines)
+        collection->entities.emplace_back(new ExtrusionPath(polyline, *this));
 }
 
 void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
@@ -67,36 +58,36 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
 
 void ExtrusionMultiPath::reverse()
 {
-    for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        path->reverse();
+    for (ExtrusionPath &path : this->paths)
+        path.reverse();
     std::reverse(this->paths.begin(), this->paths.end());
 }
 
 double ExtrusionMultiPath::length() const
 {
     double len = 0;
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        len += path->polyline.length();
+    for (const ExtrusionPath &path : this->paths)
+        len += path.polyline.length();
     return len;
 }
 
 void ExtrusionMultiPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
 {
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        path->polygons_covered_by_width(out, scaled_epsilon);
+    for (const ExtrusionPath &path : this->paths)
+        path.polygons_covered_by_width(out, scaled_epsilon);
 }
 
 void ExtrusionMultiPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
 {
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        path->polygons_covered_by_spacing(out, scaled_epsilon);
+    for (const ExtrusionPath &path : this->paths)
+        path.polygons_covered_by_spacing(out, scaled_epsilon);
 }
 
 double ExtrusionMultiPath::min_mm3_per_mm() const
 {
     double min_mm3_per_mm = std::numeric_limits<double>::max();
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm);
+    for (const ExtrusionPath &path : this->paths)
+        min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm);
     return min_mm3_per_mm;
 }
 
@@ -121,52 +112,46 @@ Polyline ExtrusionMultiPath::as_polyline() const
     return out;
 }
 
-bool
-ExtrusionLoop::make_clockwise()
+bool ExtrusionLoop::make_clockwise()
 {
     bool was_ccw = this->polygon().is_counter_clockwise();
     if (was_ccw) this->reverse();
     return was_ccw;
 }
 
-bool
-ExtrusionLoop::make_counter_clockwise()
+bool ExtrusionLoop::make_counter_clockwise()
 {
     bool was_cw = this->polygon().is_clockwise();
     if (was_cw) this->reverse();
     return was_cw;
 }
 
-void
-ExtrusionLoop::reverse()
+void ExtrusionLoop::reverse()
 {
-    for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        path->reverse();
+    for (ExtrusionPath &path : this->paths)
+        path.reverse();
     std::reverse(this->paths.begin(), this->paths.end());
 }
 
-Polygon
-ExtrusionLoop::polygon() const
+Polygon ExtrusionLoop::polygon() const
 {
     Polygon polygon;
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
+    for (const ExtrusionPath &path : this->paths) {
         // for each polyline, append all points except the last one (because it coincides with the first one of the next polyline)
-        polygon.points.insert(polygon.points.end(), path->polyline.points.begin(), path->polyline.points.end()-1);
+        polygon.points.insert(polygon.points.end(), path.polyline.points.begin(), path.polyline.points.end()-1);
     }
     return polygon;
 }
 
-double
-ExtrusionLoop::length() const
+double ExtrusionLoop::length() const
 {
     double len = 0;
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        len += path->polyline.length();
+    for (const ExtrusionPath &path : this->paths)
+        len += path.polyline.length();
     return len;
 }
 
-bool
-ExtrusionLoop::split_at_vertex(const Point &point)
+bool ExtrusionLoop::split_at_vertex(const Point &point)
 {
     for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
         int idx = path->polyline.find_point(point);
@@ -220,18 +205,18 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
         Point  p_non_overhang;
         size_t path_idx_non_overhang = 0;
         double min_non_overhang = std::numeric_limits<double>::max();
-        for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
-            Point p_tmp = point.projection_onto(path->polyline);
+        for (const ExtrusionPath &path : this->paths) {
+            Point p_tmp = point.projection_onto(path.polyline);
             double dist = (p_tmp - point).cast<double>().norm();
             if (dist < min) {
                 p = p_tmp;
                 min = dist;
-                path_idx = path - this->paths.begin();
+                path_idx = &path - &this->paths.front();
             } 
-            if (prefer_non_overhang && ! is_bridge(path->role()) && dist < min_non_overhang) {
+            if (prefer_non_overhang && ! is_bridge(path.role()) && dist < min_non_overhang) {
                 p_non_overhang = p_tmp;
                 min_non_overhang = dist;
-                path_idx_non_overhang = path - this->paths.begin();
+                path_idx_non_overhang = &path - &this->paths.front();
             }
         }
         if (prefer_non_overhang && min_non_overhang != std::numeric_limits<double>::max()) {
@@ -267,8 +252,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
     this->split_at_vertex(p);
 }
 
-void
-ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
+void ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
 {
     *paths = this->paths;
     
@@ -285,15 +269,14 @@ ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
     }
 }
 
-bool
-ExtrusionLoop::has_overhang_point(const Point &point) const
+bool ExtrusionLoop::has_overhang_point(const Point &point) const
 {
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
-        int pos = path->polyline.find_point(point);
+    for (const ExtrusionPath &path : this->paths) {
+        int pos = path.polyline.find_point(point);
         if (pos != -1) {
             // point belongs to this path
             // we consider it overhang only if it's not an endpoint
-            return (is_bridge(path->role()) && pos > 0 && pos != (int)(path->polyline.points.size())-1);
+            return (is_bridge(path.role()) && pos > 0 && pos != (int)(path.polyline.points.size())-1);
         }
     }
     return false;
@@ -301,22 +284,21 @@ ExtrusionLoop::has_overhang_point(const Point &point) const
 
 void ExtrusionLoop::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
 {
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        path->polygons_covered_by_width(out, scaled_epsilon);
+    for (const ExtrusionPath &path : this->paths)
+        path.polygons_covered_by_width(out, scaled_epsilon);
 }
 
 void ExtrusionLoop::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
 {
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        path->polygons_covered_by_spacing(out, scaled_epsilon);
+    for (const ExtrusionPath &path : this->paths)
+        path.polygons_covered_by_spacing(out, scaled_epsilon);
 }
 
-double
-ExtrusionLoop::min_mm3_per_mm() const
+double ExtrusionLoop::min_mm3_per_mm() const
 {
     double min_mm3_per_mm = std::numeric_limits<double>::max();
-    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
-        min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm);
+    for (const ExtrusionPath &path : this->paths)
+        min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm);
     return min_mm3_per_mm;
 }
 
@@ -344,15 +326,4 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
     return "";
 }
 
-//std::string ExtrusionLoop::role_to_string(ExtrusionLoopRole role)
-//{
-//    switch (role) {
-//        case elrDefault                 : return "elrDefault";
-//        case elrContourInternalPerimeter: return "elrContourInternalPerimeter";
-//        case elrSkirt                   : return "elrSkirt";
-//        default                         : assert(false);
-//    }
-//};
-
-
 }
diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index 2a66489fe..ce52ae152 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -75,6 +75,8 @@ public:
     virtual bool is_loop() const { return false; }
     virtual bool can_reverse() const { return true; }
     virtual ExtrusionEntity* clone() const = 0;
+    // Create a new object, initialize it with this object using the move semantics.
+    virtual ExtrusionEntity* clone_move() = 0;
     virtual ~ExtrusionEntity() {}
     virtual void reverse() = 0;
     virtual Point first_point() const = 0;
@@ -123,13 +125,17 @@ public:
     ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}
     ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}
     ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
-    ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
+	ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
+	ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
+	ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
 //    ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {};
 
     ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; }
     ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; }
 
-    ExtrusionPath* clone() const override { return new ExtrusionPath (*this); }
+	ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); }
+    // Create a new object, initialize it with this object using the move semantics.
+	ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); }
     void reverse() override { this->polyline.reverse(); }
     Point first_point() const override { return this->polyline.points.front(); }
     Point last_point() const override { return this->polyline.points.back(); }
@@ -188,7 +194,9 @@ public:
 
     bool is_loop() const override { return false; }
     bool can_reverse() const override { return true; }
-    ExtrusionMultiPath* clone() const override { return new ExtrusionMultiPath(*this); }
+	ExtrusionEntity* clone() const override { return new ExtrusionMultiPath(*this); }
+    // Create a new object, initialize it with this object using the move semantics.
+	ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
     void reverse() override;
     Point first_point() const override { return this->paths.front().polyline.points.front(); }
     Point last_point() const override { return this->paths.back().polyline.points.back(); }
@@ -227,7 +235,9 @@ public:
         { this->paths.emplace_back(std::move(path)); }
     bool is_loop() const override{ return true; }
     bool can_reverse() const override { return false; }
-    ExtrusionLoop* clone() const override{ return new ExtrusionLoop (*this); }
+	ExtrusionEntity* clone() const override{ return new ExtrusionLoop (*this); }
+    // Create a new object, initialize it with this object using the move semantics.
+	ExtrusionEntity* clone_move() override { return new ExtrusionLoop(std::move(*this)); }
     bool make_clockwise();
     bool make_counter_clockwise();
     void reverse() override;
diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp
index 7a086bcbf..9ae116c47 100644
--- a/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -21,8 +21,7 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const Extrusion
     return *this;
 }
 
-void
-ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c)
+void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c)
 {
     std::swap(this->entities, c.entities);
     std::swap(this->orig_indices, c.orig_indices);
@@ -39,15 +38,14 @@ void ExtrusionEntityCollection::clear()
 ExtrusionEntityCollection::operator ExtrusionPaths() const
 {
     ExtrusionPaths paths;
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
-        if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(*it))
+    for (const ExtrusionEntity *ptr : this->entities) {
+        if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath*>(ptr))
             paths.push_back(*path);
     }
     return paths;
 }
 
-ExtrusionEntityCollection*
-ExtrusionEntityCollection::clone() const
+ExtrusionEntity* ExtrusionEntityCollection::clone() const
 {
     ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(*this);
     for (size_t i = 0; i < coll->entities.size(); ++i)
@@ -55,41 +53,36 @@ ExtrusionEntityCollection::clone() const
     return coll;
 }
 
-void
-ExtrusionEntityCollection::reverse()
+void ExtrusionEntityCollection::reverse()
 {
-    for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
+    for (ExtrusionEntity *ptr : this->entities)
         // Don't reverse it if it's a loop, as it doesn't change anything in terms of elements ordering
         // and caller might rely on winding order
-        if (!(*it)->is_loop()) (*it)->reverse();
-    }
+        if (! ptr->is_loop())
+        	ptr->reverse();
     std::reverse(this->entities.begin(), this->entities.end());
 }
 
-void
-ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity)
+void ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity)
 {
     delete this->entities[i];
     this->entities[i] = entity.clone();
 }
 
-void
-ExtrusionEntityCollection::remove(size_t i)
+void ExtrusionEntityCollection::remove(size_t i)
 {
     delete this->entities[i];
     this->entities.erase(this->entities.begin() + i);
 }
 
-ExtrusionEntityCollection
-ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const
+ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const
 {
     ExtrusionEntityCollection coll;
     this->chained_path(&coll, no_reverse, role);
     return coll;
 }
 
-void
-ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const
+void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const
 {
     if (this->entities.empty()) return;
     this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices);
@@ -108,6 +101,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
         *retval = *this;
         return;
     }
+
     retval->entities.reserve(this->entities.size());
     retval->orig_indices.reserve(this->entities.size());
     
@@ -115,10 +109,10 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
     std::map<ExtrusionEntity*,size_t> indices_map;
     
     ExtrusionEntitiesPtr my_paths;
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
+    for (ExtrusionEntity * const &entity_src : this->entities) {
         if (role != erMixed) {
             // The caller wants only paths with a specific extrusion role.
-            auto role2 = (*it)->role();
+            auto role2 = entity_src->role();
             if (role != role2) {
                 // This extrusion entity does not match the role asked.
                 assert(role2 != erMixed);
@@ -126,32 +120,30 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
             }
         }
 
-        ExtrusionEntity* entity = (*it)->clone();
+        ExtrusionEntity *entity = entity_src->clone();
         my_paths.push_back(entity);
-        if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
+        if (orig_indices != nullptr)
+        	indices_map[entity] = &entity_src - &this->entities.front();
     }
     
     Points endpoints;
-    for (ExtrusionEntitiesPtr::iterator it = my_paths.begin(); it != my_paths.end(); ++it) {
-        endpoints.push_back((*it)->first_point());
-        if (no_reverse || !(*it)->can_reverse()) {
-            endpoints.push_back((*it)->first_point());
-        } else {
-            endpoints.push_back((*it)->last_point());
-        }
+    for (const ExtrusionEntity *entity : my_paths) {
+        endpoints.push_back(entity->first_point());
+        endpoints.push_back((no_reverse || ! entity->can_reverse()) ?
+        	entity->first_point() : entity->last_point());
     }
     
-    while (!my_paths.empty()) {
+    while (! my_paths.empty()) {
         // find nearest point
         int start_index = start_near.nearest_point_index(endpoints);
         int path_index = start_index/2;
         ExtrusionEntity* entity = my_paths.at(path_index);
         // never reverse loops, since it's pointless for chained path and callers might depend on orientation
-        if (start_index % 2 && !no_reverse && entity->can_reverse()) {
+        if (start_index % 2 && !no_reverse && entity->can_reverse())
             entity->reverse();
-        }
         retval->entities.push_back(my_paths.at(path_index));
-        if (orig_indices != NULL) orig_indices->push_back(indices_map[entity]);
+        if (orig_indices != nullptr)
+        	orig_indices->push_back(indices_map[entity]);
         my_paths.erase(my_paths.begin() + path_index);
         endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
         start_near = retval->entities.back()->last_point();
@@ -160,60 +152,50 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
 
 void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
 {
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it)
-        (*it)->polygons_covered_by_width(out, scaled_epsilon);
+    for (const ExtrusionEntity *entity : this->entities)
+        entity->polygons_covered_by_width(out, scaled_epsilon);
 }
 
 void ExtrusionEntityCollection::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
 {
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it)
-        (*it)->polygons_covered_by_spacing(out, scaled_epsilon);
+    for (const ExtrusionEntity *entity : this->entities)
+        entity->polygons_covered_by_spacing(out, scaled_epsilon);
 }
 
-/* Recursively count paths and loops contained in this collection */
-size_t
-ExtrusionEntityCollection::items_count() const
+// Recursively count paths and loops contained in this collection.
+size_t ExtrusionEntityCollection::items_count() const
 {
     size_t count = 0;
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
-        if ((*it)->is_collection()) {
-            ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it);
-            count += collection->items_count();
-        } else {
-            ++count;
-        }
-    }
+    for (const ExtrusionEntity *entity : this->entities)
+        if (entity->is_collection())
+            count += static_cast<const ExtrusionEntityCollection*>(entity)->items_count();
+        else
+            ++ count;
     return count;
 }
 
-/* Returns a single vector of pointers to all non-collection items contained in this one */
-void
-ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const
+// Returns a single vector of pointers to all non-collection items contained in this one.
+void ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const
 {
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
-        if ((*it)->is_collection()) {
-            ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it);
-            retval->append(collection->flatten().entities);
-        } else {
-            retval->append(**it);
-        }
-    }
+    for (const ExtrusionEntity *entity : this->entities)
+        if (entity->is_collection())
+            retval->append(static_cast<const ExtrusionEntityCollection*>(entity)->flatten().entities);
+        else
+            retval->append(*entity);
 }
 
-ExtrusionEntityCollection
-ExtrusionEntityCollection::flatten() const
+ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const
 {
     ExtrusionEntityCollection coll;
     this->flatten(&coll);
     return coll;
 }
 
-double
-ExtrusionEntityCollection::min_mm3_per_mm() const
+double ExtrusionEntityCollection::min_mm3_per_mm() const
 {
     double min_mm3_per_mm = std::numeric_limits<double>::max();
-    for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it)
-        min_mm3_per_mm = std::min(min_mm3_per_mm, (*it)->min_mm3_per_mm());
+    for (const ExtrusionEntity *entity : this->entities)
+    	min_mm3_per_mm = std::min(min_mm3_per_mm, entity->min_mm3_per_mm());
     return min_mm3_per_mm;
 }
 
diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp
index 230c04160..4fe964ee1 100644
--- a/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -9,7 +9,10 @@ namespace Slic3r {
 class ExtrusionEntityCollection : public ExtrusionEntity
 {
 public:
-    ExtrusionEntityCollection* clone() const;
+    ExtrusionEntity* clone() const override;
+    // Create a new object, initialize it with this object using the move semantics.
+	ExtrusionEntity* clone_move() override { return new ExtrusionEntityCollection(std::move(*this)); }
+
     ExtrusionEntitiesPtr entities;     // we own these entities
     std::vector<size_t> orig_indices;  // handy for XS
     bool no_sort;
@@ -36,11 +39,12 @@ public:
     bool empty() const { return this->entities.empty(); };
     void clear();
     void swap (ExtrusionEntityCollection &c);
-    void append(const ExtrusionEntity &entity) { this->entities.push_back(entity.clone()); }
-    void append(const ExtrusionEntitiesPtr &entities) { 
+    void append(const ExtrusionEntity &entity) { this->entities.emplace_back(entity.clone()); }
+    void append(ExtrusionEntity &&entity) { this->entities.emplace_back(entity.clone_move()); }
+    void append(const ExtrusionEntitiesPtr &entities) {
         this->entities.reserve(this->entities.size() + entities.size());
-        for (ExtrusionEntitiesPtr::const_iterator ptr = entities.begin(); ptr != entities.end(); ++ptr)
-            this->entities.push_back((*ptr)->clone());
+        for (const ExtrusionEntity *ptr : entities)
+            this->entities.emplace_back(ptr->clone());
     }
     void append(ExtrusionEntitiesPtr &&src) {
         if (entities.empty())
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index 793657817..0c16f4a1d 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -189,42 +189,36 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
         coll.append(ExtrusionLoop(paths, loop_role));
     }
     
-    // append thin walls to the nearest-neighbor search (only for first iteration)
-    if (!thin_walls.empty()) {
+    // Append thin walls to the nearest-neighbor search (only for first iteration)
+    if (! thin_walls.empty()) {
         ExtrusionEntityCollection tw = variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow);
         coll.append(tw.entities);
         thin_walls.clear();
     }
     
-    // sort entities into a new collection using a nearest-neighbor search,
-    // preserving the original indices which are useful for detecting thin walls
+    // Sort entities into a new collection using a nearest-neighbor search,
+    // preserving the original indices which are useful for detecting thin walls.
     ExtrusionEntityCollection sorted_coll;
     coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices);
     
     // traverse children and build the final collection
     ExtrusionEntityCollection entities;
-    for (std::vector<size_t>::const_iterator idx = sorted_coll.orig_indices.begin();
-        idx != sorted_coll.orig_indices.end();
-        ++idx) {
-        
-        if (*idx >= loops.size()) {
-            // this is a thin wall
-            // let's get it from the sorted collection as it might have been reversed
-            size_t i = idx - sorted_coll.orig_indices.begin();
-            entities.append(*sorted_coll.entities[i]);
+    for (const size_t &idx : sorted_coll.orig_indices) {
+        if (idx >= loops.size()) {
+            // This is a thin wall. Let's get it from the sorted collection as it might have been reversed.
+            entities.append(std::move(*sorted_coll.entities[&idx - &sorted_coll.orig_indices.front()]));
         } else {
-            const PerimeterGeneratorLoop &loop = loops[*idx];
-            ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]);
-            
+            const PerimeterGeneratorLoop &loop = loops[idx];
+            ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[idx]);
             ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
             if (loop.is_contour) {
                 eloop.make_counter_clockwise();
-                entities.append(children.entities);
-                entities.append(eloop);
+                entities.append(std::move(children.entities));
+                entities.append(std::move(eloop));
             } else {
                 eloop.make_clockwise();
-                entities.append(eloop);
-                entities.append(children.entities);
+                entities.append(std::move(eloop));
+                entities.append(std::move(children.entities));
             }
         }
     }

From 177a96a768b7fb9abd37b39d47b9a3a1c9e976af Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Fri, 13 Sep 2019 11:01:02 +0200
Subject: [PATCH 05/10] Fix arrange crash with ASAN

---
 .../libnest2d/selections/selection_boilerplate.hpp     | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
index 4bb2e72af..2df9a26c3 100644
--- a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
+++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
@@ -33,10 +33,16 @@ protected:
         // then it should be removed from the list
         auto it = c.begin();
         while (it != c.end() && !stopcond_()) {
-            Placer p{bin};
-            p.configure(pcfg);
+            
+            // WARNING: The copy of itm needs to be created before Placer.
+            // Placer is working with references and its destructor still
+            // manipulates the item this is why the order of stack creation
+            // matters here.            
             const Item& itm = *it;
             Item cpy{itm};
+            
+            Placer p{bin};
+            p.configure(pcfg);
             if (!p.pack(cpy)) it = c.erase(it);
             else it++;
         }

From cf23146ee3fa6e6038396d85796d497f9edfd8ab Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 12 Sep 2019 13:18:02 +0200
Subject: [PATCH 06/10] Refactoring the SLA clipping plane The plane is now
 internally stored as a plane in world coordinates

---
 src/slic3r/GUI/GLCanvas3D.hpp                | 22 +++++-
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 70 ++++++++------------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp |  9 ++-
 3 files changed, 53 insertions(+), 48 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 202d3ef77..1b998c67e 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -89,11 +89,29 @@ public:
         m_data[3] = offset;
     }
 
+    bool operator==(const ClippingPlane& cp) const {
+        return m_data[0]==cp.m_data[0] && m_data[1]==cp.m_data[1] && m_data[2]==cp.m_data[2] && m_data[3]==cp.m_data[3];
+    }
+    bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); }
+
+    double distance(const Vec3d& pt) const {
+        assert(is_approx(get_normal().norm(), 1.));
+        return (-get_normal().dot(pt) + m_data[3]);
+    }
+
+    void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
+    void set_offset(double offset) { m_data[3] = offset; }
+    Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
     bool is_active() const { return m_data[3] != DBL_MAX; }
-
     static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
-
     const double* get_data() const { return m_data; }
+
+    // Serialization through cereal library
+    template <class Archive>
+    void serialize( Archive & ar )
+    {
+        ar( m_data[0], m_data[1], m_data[2], m_data[3] );
+    }
 };
 
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 0a5c21cce..404db33dd 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -26,6 +26,8 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& ic
     , m_quadric(nullptr)
     , m_its(nullptr)
 {
+    m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
+    m_old_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
     m_quadric = ::gluNewQuadric();
     if (m_quadric != nullptr)
         // using GLU_FILL does not work when the instance's transformation
@@ -137,10 +139,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
     if (m_clipping_plane_distance == 0.f)
         return;
 
-    if (m_clipping_plane_normal == Vec3d::Zero())
-        reset_clipping_plane_normal();
-
-    const Vec3d& direction_to_camera = m_clipping_plane_normal;
+    const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
 
     // First cache instance transformation to be used later.
     const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
@@ -148,27 +147,24 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
     Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast<float>();
     Vec3f scaling = vol->get_instance_scaling_factor().cast<float>();
     Vec3d instance_offset = vol->get_instance_offset();
-
     // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
-    Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast<float>();
+    Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_clipping_plane->get_normal().cast<float>();
     Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
-    float height_mesh = (m_active_instance_bb_radius - m_clipping_plane_distance * 2*m_active_instance_bb_radius) * (up_noscale.norm()/up.norm());
+    float height_mesh = m_clipping_plane->distance(center) * (up_noscale.norm()/up.norm());
 
     // Get transformation of the supports and calculate how far from its origin the clipping plane is.
     Transform3d supports_trafo = Transform3d::Identity();
     supports_trafo = supports_trafo.rotate(Eigen::AngleAxisd(vol->get_instance_rotation()(2), Vec3d::UnitZ()));
-    Vec3f up_supports = (supports_trafo.inverse() * direction_to_camera).cast<float>();
+    Vec3f up_supports = (supports_trafo.inverse() * m_clipping_plane->get_normal()).cast<float>();
     supports_trafo = supports_trafo.pretranslate(Vec3d(instance_offset(0), instance_offset(1), vol->get_sla_shift_z()));
     // Instance and supports origin do not coincide, so the following is quite messy:
-    float height_supports = height_mesh * (up.norm() / up_supports.norm()) + instance_offset(2) * (direction_to_camera(2) / direction_to_camera.norm());
+    float height_supports = height_mesh * (up.norm() / up_supports.norm()) + instance_offset(2) * (m_clipping_plane->get_normal()(2));
 
     // In case either of these was recently changed, the cached triangulated ExPolygons are invalid now.
     // We are gonna recalculate them both for the object and for the support structures.
-    if (m_clipping_plane_distance != m_old_clipping_plane_distance
-     || m_old_clipping_plane_normal != direction_to_camera) {
+    if (*m_old_clipping_plane != *m_clipping_plane) {
 
-        m_old_clipping_plane_normal = direction_to_camera;
-        m_old_clipping_plane_distance = m_clipping_plane_distance;
+        *m_old_clipping_plane = *m_clipping_plane;
 
         // Now initialize the TMS for the object, perform the cut and save the result.
         if (! m_tms) {
@@ -379,15 +375,12 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
 
 bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
 {
-    const Vec3d& direction_to_camera = m_clipping_plane_normal;
-
     if (m_clipping_plane_distance == 0.f)
         return false;
 
     Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
     transformed_point(2) += m_z_shift;
-    return direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift)) + m_active_instance_bb_radius
-            - m_clipping_plane_distance * 2*m_active_instance_bb_radius < direction_to_camera.dot(transformed_point);
+    return m_clipping_plane->distance(transformed_point) < 0.;
 }
 
 
@@ -693,18 +686,18 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 
     if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
         m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f);
-        m_parent.set_as_dirty();
+        update_clipping_plane(true);
         return true;
     }
 
     if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
         m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f);
-        m_parent.set_as_dirty();
+        update_clipping_plane(true);
         return true;
     }
 
     if (action == SLAGizmoEventType::ResetClippingPlane) {
-        reset_clipping_plane_normal();
+        update_clipping_plane();
         return true;
     }
 
@@ -796,18 +789,10 @@ void GLGizmoSlaSupports::update_cache_entry_normal(size_t i) const
 
 ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
 {
-    if (!m_model_object || m_state == Off)
+    if (!m_model_object || m_state == Off || m_clipping_plane_distance == 0.f)
         return ClippingPlane::ClipsNothing();
-
-    //Eigen::Matrix<GLdouble, 4, 4, Eigen::DontAlign> modelview_matrix;
-    //::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
-    // we'll recover current look direction from the modelview matrix (in world coords):
-    //Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
-
-    const Vec3d& direction_to_camera = m_clipping_plane_normal;
-    float dist = direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift));
-
-    return ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius));
+    else
+        return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]);
 }
 
 
@@ -1019,14 +1004,15 @@ RENDER_AGAIN:
     else {
         if (m_imgui->button(m_desc.at("reset_direction"))) {
             wxGetApp().CallAfter([this](){
-                    reset_clipping_plane_normal();
+                    update_clipping_plane();
                 });
         }
     }
 
     ImGui::SameLine(clipping_slider_left);
     ImGui::PushItemWidth(window_width - clipping_slider_left);
-    ImGui::SliderFloat("  ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f");
+    if (ImGui::SliderFloat("  ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f"))
+        update_clipping_plane(true);
 
 
     if (m_imgui->button("?")) {
@@ -1198,7 +1184,7 @@ void GLGizmoSlaSupports::on_stop_dragging()
 void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
 {
     ar(m_clipping_plane_distance,
-       m_clipping_plane_normal,
+       *m_clipping_plane,
        m_model_object_id,
        m_new_point_head_diameter,
        m_normal_cache,
@@ -1212,7 +1198,7 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
 void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const
 {
     ar(m_clipping_plane_distance,
-       m_clipping_plane_normal,
+       *m_clipping_plane,
        m_model_object_id,
        m_new_point_head_diameter,
        m_normal_cache,
@@ -1401,17 +1387,19 @@ bool GLGizmoSlaSupports::unsaved_changes() const
 }
 
 
-void GLGizmoSlaSupports::reset_clipping_plane_normal() const
+void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const
 {
-    Eigen::Matrix<double, 4, 4, Eigen::DontAlign> modelview_matrix;
-    ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
-    m_clipping_plane_normal = Vec3d(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
+    Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ?
+                        m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward());
+
+    const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
+    float dist = normal.dot(center);
+    *m_clipping_plane = ClippingPlane(normal, (dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius));
     m_parent.set_as_dirty();
 }
 
-
 SlaGizmoHelpDialog::SlaGizmoHelpDialog()
-: wxDialog(NULL, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
+: wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
 {
     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
     const wxString ctrl = GUI::shortkey_ctrl_prefix();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index d2451f64e..ee8e55218 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -4,6 +4,7 @@
 #include "GLGizmoBase.hpp"
 #include "GLGizmos.hpp"
 #include "slic3r/GUI/GLSelectionRectangle.hpp"
+#include "slic3r/GUI/GLSelectionRectangle.hpp"
 
 // There is an L function in igl that would be overridden by our localization macro - let's undefine it...
 #undef L
@@ -19,10 +20,8 @@
 namespace Slic3r {
 namespace GUI {
 
-
 class ClippingPlane;
 
-
 class GLGizmoSlaSupports : public GLGizmoBase
 {
 private:
@@ -114,9 +113,8 @@ private:
     std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
 
     float m_clipping_plane_distance = 0.f;
-    mutable float m_old_clipping_plane_distance = 0.f;
-    mutable Vec3d m_old_clipping_plane_normal;
-    mutable Vec3d m_clipping_plane_normal = Vec3d::Zero();
+    std::unique_ptr<ClippingPlane> m_clipping_plane;
+    std::unique_ptr<ClippingPlane> m_old_clipping_plane;
 
     // This map holds all translated description texts, so they can be easily referenced during layout calculations
     // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
@@ -151,6 +149,7 @@ private:
     void switch_to_editing_mode();
     void disable_editing_mode();
     void reset_clipping_plane_normal() const;
+    void update_clipping_plane(bool keep_normal = false) const;
 
 protected:
     void on_set_state() override;

From 546917830bb1f96615ee5841518c717fc348b9a0 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 12 Sep 2019 14:58:03 +0200
Subject: [PATCH 07/10] Initial implementation of MeshClipper class So far the
 work is shared between the old code in GLGizmoSlaSupports.cpp and the new
 class

---
 src/slic3r/CMakeLists.txt                    |  2 +
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 51 ++++++------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp |  5 +-
 src/slic3r/GUI/MeshClipper.cpp               | 83 ++++++++++++++++++++
 src/slic3r/GUI/MeshClipper.hpp               | 37 +++++++++
 5 files changed, 149 insertions(+), 29 deletions(-)
 create mode 100644 src/slic3r/GUI/MeshClipper.cpp
 create mode 100644 src/slic3r/GUI/MeshClipper.hpp

diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 161e1a1ff..8587f01f2 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -85,6 +85,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/GUI_ObjectLayers.hpp
     GUI/LambdaObjectDialog.cpp
     GUI/LambdaObjectDialog.hpp
+    GUI/MeshClipper.cpp
+    GUI/MeshClipper.hpp
     GUI/Tab.cpp
     GUI/Tab.hpp
     GUI/ConfigManipulation.cpp
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 404db33dd..ce86ffc55 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -12,6 +12,7 @@
 #include "slic3r/GUI/GUI.hpp"
 #include "slic3r/GUI/GUI_ObjectSettings.hpp"
 #include "slic3r/GUI/GUI_ObjectList.hpp"
+#include "slic3r/GUI/MeshClipper.hpp"
 #include "slic3r/GUI/Plater.hpp"
 #include "slic3r/GUI/PresetBundle.hpp"
 #include "libslic3r/SLAPrint.hpp"
@@ -143,8 +144,9 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
 
     // First cache instance transformation to be used later.
     const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
-    Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast<float>();
-    Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast<float>();
+    Geometry::Transformation trafo = vol->get_instance_transformation();
+    Transform3f instance_matrix = trafo.get_matrix().cast<float>();
+    Transform3f instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true).cast<float>();
     Vec3f scaling = vol->get_instance_scaling_factor().cast<float>();
     Vec3d instance_offset = vol->get_instance_offset();
     // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
@@ -152,6 +154,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
     Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
     float height_mesh = m_clipping_plane->distance(center) * (up_noscale.norm()/up.norm());
 
+
     // Get transformation of the supports and calculate how far from its origin the clipping plane is.
     Transform3d supports_trafo = Transform3d::Identity();
     supports_trafo = supports_trafo.rotate(Eigen::AngleAxisd(vol->get_instance_rotation()(2), Vec3d::UnitZ()));
@@ -167,15 +170,13 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
         *m_old_clipping_plane = *m_clipping_plane;
 
         // Now initialize the TMS for the object, perform the cut and save the result.
-        if (! m_tms) {
-            m_tms.reset(new TriangleMeshSlicer);
-            m_tms->init(m_mesh, [](){});
+        if (! m_object_clipper) {
+            m_object_clipper.reset(new MeshClipper);
+            m_object_clipper->set_mesh(*m_mesh);
         }
-        std::vector<ExPolygons> list_of_expolys;
-        m_tms->set_up_direction(up);
-        m_tms->slice(std::vector<float>{height_mesh}, 0.f, &list_of_expolys, [](){});
-        m_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
-
+        m_object_clipper->set_plane(*m_clipping_plane);
+        trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
+        m_object_clipper->set_transformation(trafo);
 
 
         // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
@@ -198,31 +199,27 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
                 // so we can later tell they were recalculated.
                 size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
 
-                if (!m_supports_tms || (int)timestamp != m_old_timestamp) {
+                if (! m_supports_clipper || (int)timestamp != m_old_timestamp) {
                     // The timestamp has changed - stash the mesh and initialize the TMS.
                     m_supports_mesh = &print_object->support_mesh();
-                    m_supports_tms.reset(new TriangleMeshSlicer);
+                    m_supports_clipper.reset(new MeshClipper);
                     // The mesh should already have the shared vertices calculated.
-                    m_supports_tms->init(m_supports_mesh, [](){});
+                    m_supports_clipper->set_mesh(*m_supports_mesh);
                     m_old_timestamp = timestamp;
                 }
 
                 // The TMS is initialized - let's do the cutting:
-                list_of_expolys.clear();
-                m_supports_tms->set_up_direction(up_supports);
-                m_supports_tms->slice(std::vector<float>{height_supports}, 0.f, &list_of_expolys, [](){});
-                m_supports_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
+                m_supports_clipper->set_plane(*m_clipping_plane);
+                m_supports_clipper->set_transformation(Geometry::Transformation(supports_trafo));
             }
-            else {
+            else
                 // The supports are not valid. We better dump the cached data.
-                m_supports_tms.reset();
-                m_supports_triangles.clear();
-            }
+                m_supports_clipper.reset();
         }
     }
 
     // At this point we have the triangulated cuts for both the object and supports - let's render.
-	if (! m_triangles.empty()) {
+    if (! m_object_clipper->get_triangles().empty()) {
 		::glPushMatrix();
 		::glTranslated(0.0, 0.0, m_z_shift);
 		::glMultMatrixf(instance_matrix.data());
@@ -233,14 +230,14 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
 		::glTranslatef(0.f, 0.f, 0.01f); // to make sure the cut does not intersect the structure itself
         ::glColor3f(1.0f, 0.37f, 0.0f);
         ::glBegin(GL_TRIANGLES);
-        for (const Vec2f& point : m_triangles)
+        for (const Vec2f& point : m_object_clipper->get_triangles())
             ::glVertex3f(point(0), point(1), height_mesh);
 
         ::glEnd();
 		::glPopMatrix();
 	}
 
-    if (! m_supports_triangles.empty() && !m_editing_mode) {
+    if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) {
         // The supports are hidden in the editing mode, so it makes no sense to render the cuts.
 		::glPushMatrix();
         ::glMultMatrixd(supports_trafo.data());
@@ -251,7 +248,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
 		::glTranslatef(0.f, 0.f, 0.01f);
         ::glColor3f(1.0f, 0.f, 0.37f);
         ::glBegin(GL_TRIANGLES);
-        for (const Vec2f& point : m_supports_triangles)
+        for (const Vec2f& point : m_supports_clipper->get_triangles())
             ::glVertex3f(point(0), point(1), height_supports);
 
         ::glEnd();
@@ -1142,8 +1139,8 @@ void GLGizmoSlaSupports::on_set_state()
             // Release triangle mesh slicer and the AABB spatial search structure.
             m_AABB.deinit();
             m_its = nullptr;
-            m_tms.reset();
-            m_supports_tms.reset();
+            m_object_clipper.reset();
+            m_supports_clipper.reset();
         }
     }
     m_old_state = m_state;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index ee8e55218..130ac5202 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -21,6 +21,7 @@ namespace Slic3r {
 namespace GUI {
 
 class ClippingPlane;
+class MeshClipper;
 
 class GLGizmoSlaSupports : public GLGizmoBase
 {
@@ -126,8 +127,8 @@ private:
     bool m_selection_empty = true;
     EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
 
-    mutable std::unique_ptr<TriangleMeshSlicer> m_tms;
-    mutable std::unique_ptr<TriangleMeshSlicer> m_supports_tms;
+    mutable std::unique_ptr<MeshClipper> m_object_clipper;
+    mutable std::unique_ptr<MeshClipper> m_supports_clipper;
 
     std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
     bool is_point_clipped(const Vec3d& point) const;
diff --git a/src/slic3r/GUI/MeshClipper.cpp b/src/slic3r/GUI/MeshClipper.cpp
new file mode 100644
index 000000000..c9648e7b6
--- /dev/null
+++ b/src/slic3r/GUI/MeshClipper.cpp
@@ -0,0 +1,83 @@
+#include "MeshClipper.hpp"
+#include "GLCanvas3D.hpp"
+#include "libslic3r/Tesselate.hpp"
+
+namespace Slic3r {
+namespace GUI {
+
+void MeshClipper::set_plane(const ClippingPlane& plane)
+{
+    if (m_plane != plane) {
+        m_plane = plane;
+        m_triangles_valid = false;
+    }
+}
+
+void MeshClipper::set_mesh(const TriangleMesh& mesh)
+{
+    if (m_mesh != &mesh) {
+        m_mesh = &mesh;
+        m_triangles_valid = false;
+        m_triangles.resize(0);
+        m_tms.reset(nullptr);
+    }
+}
+
+void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
+{
+    if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) {
+        m_trafo = trafo;
+        m_triangles_valid = false;
+        m_triangles.resize(0);
+    }
+}
+
+
+const std::vector<Vec2f>& MeshClipper::get_triangles()
+{
+    if (! m_triangles_valid)
+        recalculate_triangles();
+
+    return m_triangles;
+}
+
+void MeshClipper::recalculate_triangles()
+{
+    if (! m_tms) {
+        m_tms.reset(new TriangleMeshSlicer);
+        m_tms->init(m_mesh, [](){});
+    }
+
+
+    auto up_and_height = get_mesh_cut_normal();
+    Vec3f up = up_and_height.first;
+    float height_mesh = up_and_height.second;
+
+    std::vector<ExPolygons> list_of_expolys;
+    m_tms->set_up_direction(up);
+    m_tms->slice(std::vector<float>{height_mesh}, 0.f, &list_of_expolys, [](){});
+    m_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
+
+    m_triangles_valid = true;
+}
+
+std::pair<Vec3f, float> MeshClipper::get_mesh_cut_normal() const
+{
+    Transform3f instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
+    Vec3f scaling = m_trafo.get_scaling_factor().cast<float>();
+
+    // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
+    Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast<float>();
+    Vec3f up (up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
+
+    float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
+
+
+    return std::make_pair(up, height_mesh);
+}
+
+
+
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/src/slic3r/GUI/MeshClipper.hpp b/src/slic3r/GUI/MeshClipper.hpp
new file mode 100644
index 000000000..dbeef4408
--- /dev/null
+++ b/src/slic3r/GUI/MeshClipper.hpp
@@ -0,0 +1,37 @@
+#ifndef slic3r_MeshClipper_hpp_
+#define slic3r_MeshClipper_hpp_
+
+#include "libslic3r/Point.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
+
+namespace Slic3r {
+namespace GUI {
+
+class MeshClipper {
+public:
+    void set_plane(const ClippingPlane& plane);
+    void set_mesh(const TriangleMesh& mesh);
+    void set_transformation(const Geometry::Transformation& trafo);
+
+    const std::vector<Vec2f>& get_triangles();
+
+private:
+    void recalculate_triangles();
+    std::pair<Vec3f, float> get_mesh_cut_normal() const;
+
+
+    Geometry::Transformation m_trafo;
+    const TriangleMesh* m_mesh = nullptr;
+    ClippingPlane m_plane;
+    std::vector<Vec2f> m_triangles;
+    bool m_triangles_valid = false;
+    std::unique_ptr<TriangleMeshSlicer> m_tms;
+};
+
+
+    
+} // namespace GUI
+} // namespace Slic3r
+
+
+#endif // slic3r_MeshClipper_hpp_

From 9782701dd4f3fe8b13c6ab0c4ed1c55f0bfdac73 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 12 Sep 2019 16:57:30 +0200
Subject: [PATCH 08/10] Calculating the transformations is now only performed
 by the MeshClipper Attempted to get mirroring right (that never worked
 correctly with the clipping plane in the sla gizmo) The transformation of the
 support mesh is kind of a mystery to me, hopefully it is right Also cleaned
 the code a bit (removed commented-out code, unused variables, etc)

---
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 140 +++++++------------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp |   1 -
 src/slic3r/GUI/MeshClipper.cpp               |  54 +++----
 src/slic3r/GUI/MeshClipper.hpp               |   7 +-
 4 files changed, 85 insertions(+), 117 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index ce86ffc55..99c4612ce 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -28,7 +28,6 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& ic
     , m_its(nullptr)
 {
     m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
-    m_old_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
     m_quadric = ::gluNewQuadric();
     if (m_quadric != nullptr)
         // using GLU_FILL does not work when the instance's transformation
@@ -140,117 +139,82 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
     if (m_clipping_plane_distance == 0.f)
         return;
 
-    const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
-
-    // First cache instance transformation to be used later.
+    // Get transformation of the instance
     const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
     Geometry::Transformation trafo = vol->get_instance_transformation();
-    Transform3f instance_matrix = trafo.get_matrix().cast<float>();
-    Transform3f instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true).cast<float>();
-    Vec3f scaling = vol->get_instance_scaling_factor().cast<float>();
-    Vec3d instance_offset = vol->get_instance_offset();
-    // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
-    Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_clipping_plane->get_normal().cast<float>();
-    Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
-    float height_mesh = m_clipping_plane->distance(center) * (up_noscale.norm()/up.norm());
+    trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
+
+    // Get transformation of supports
+    Geometry::Transformation supports_trafo;
+    supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z()));
+    supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
+    // I don't know why, but following seems to be correct.
+    supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
+                                    1,
+                                    1.));
+
+    // Now initialize the TMS for the object, perform the cut and save the result.
+    if (! m_object_clipper) {
+        m_object_clipper.reset(new MeshClipper);
+        m_object_clipper->set_mesh(*m_mesh);
+    }
+    m_object_clipper->set_plane(*m_clipping_plane);
+    m_object_clipper->set_transformation(trafo);
 
 
-    // Get transformation of the supports and calculate how far from its origin the clipping plane is.
-    Transform3d supports_trafo = Transform3d::Identity();
-    supports_trafo = supports_trafo.rotate(Eigen::AngleAxisd(vol->get_instance_rotation()(2), Vec3d::UnitZ()));
-    Vec3f up_supports = (supports_trafo.inverse() * m_clipping_plane->get_normal()).cast<float>();
-    supports_trafo = supports_trafo.pretranslate(Vec3d(instance_offset(0), instance_offset(1), vol->get_sla_shift_z()));
-    // Instance and supports origin do not coincide, so the following is quite messy:
-    float height_supports = height_mesh * (up.norm() / up_supports.norm()) + instance_offset(2) * (m_clipping_plane->get_normal()(2));
-
-    // In case either of these was recently changed, the cached triangulated ExPolygons are invalid now.
-    // We are gonna recalculate them both for the object and for the support structures.
-    if (*m_old_clipping_plane != *m_clipping_plane) {
-
-        *m_old_clipping_plane = *m_clipping_plane;
-
-        // Now initialize the TMS for the object, perform the cut and save the result.
-        if (! m_object_clipper) {
-            m_object_clipper.reset(new MeshClipper);
-            m_object_clipper->set_mesh(*m_mesh);
+    // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
+    // First we need a pointer to the respective SLAPrintObject. The index into objects vector is
+    // cached so we don't have todo it on each render. We only search for the po if needed:
+    if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) {
+        m_print_objects_count = m_parent.sla_print()->objects().size();
+        m_print_object_idx = -1;
+        for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
+            ++m_print_object_idx;
+            if (po->model_object()->id() == m_model_object->id())
+                break;
         }
-        m_object_clipper->set_plane(*m_clipping_plane);
-        trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
-        m_object_clipper->set_transformation(trafo);
+    }
+    if (m_print_object_idx >= 0) {
+        const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx];
 
+        if (print_object->is_step_done(slaposSupportTree)) {
+            // If the supports are already calculated, save the timestamp of the respective step
+            // so we can later tell they were recalculated.
+            size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
 
-        // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
-        // First we need a pointer to the respective SLAPrintObject. The index into objects vector is
-        // cached so we don't have todo it on each render. We only search for the po if needed:
-        if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) {
-            m_print_objects_count = m_parent.sla_print()->objects().size();
-            m_print_object_idx = -1;
-            for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
-                ++m_print_object_idx;
-                if (po->model_object()->id() == m_model_object->id())
-                    break;
+            if (! m_supports_clipper || (int)timestamp != m_old_timestamp) {
+                // The timestamp has changed.
+                m_supports_clipper.reset(new MeshClipper);
+                // The mesh should already have the shared vertices calculated.
+                m_supports_clipper->set_mesh(print_object->support_mesh());
+                m_old_timestamp = timestamp;
             }
+            m_supports_clipper->set_plane(*m_clipping_plane);
+            m_supports_clipper->set_transformation(supports_trafo);
         }
-        if (m_print_object_idx >= 0) {
-            const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx];
-
-            if (print_object->is_step_done(slaposSupportTree)) {
-                // If the supports are already calculated, save the timestamp of the respective step
-                // so we can later tell they were recalculated.
-                size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
-
-                if (! m_supports_clipper || (int)timestamp != m_old_timestamp) {
-                    // The timestamp has changed - stash the mesh and initialize the TMS.
-                    m_supports_mesh = &print_object->support_mesh();
-                    m_supports_clipper.reset(new MeshClipper);
-                    // The mesh should already have the shared vertices calculated.
-                    m_supports_clipper->set_mesh(*m_supports_mesh);
-                    m_old_timestamp = timestamp;
-                }
-
-                // The TMS is initialized - let's do the cutting:
-                m_supports_clipper->set_plane(*m_clipping_plane);
-                m_supports_clipper->set_transformation(Geometry::Transformation(supports_trafo));
-            }
-            else
-                // The supports are not valid. We better dump the cached data.
-                m_supports_clipper.reset();
-        }
+        else
+            // The supports are not valid. We better dump the cached data.
+            m_supports_clipper.reset();
     }
 
     // At this point we have the triangulated cuts for both the object and supports - let's render.
     if (! m_object_clipper->get_triangles().empty()) {
 		::glPushMatrix();
-		::glTranslated(0.0, 0.0, m_z_shift);
-		::glMultMatrixf(instance_matrix.data());
-		Eigen::Quaternionf q;
-		q.setFromTwoVectors(Vec3f::UnitZ(), up);
-		Eigen::AngleAxisf aa(q);
-		::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
-		::glTranslatef(0.f, 0.f, 0.01f); // to make sure the cut does not intersect the structure itself
         ::glColor3f(1.0f, 0.37f, 0.0f);
         ::glBegin(GL_TRIANGLES);
-        for (const Vec2f& point : m_object_clipper->get_triangles())
-            ::glVertex3f(point(0), point(1), height_mesh);
-
+        for (const Vec3f& point : m_object_clipper->get_triangles())
+            ::glVertex3f(point(0), point(1), point(2));
         ::glEnd();
 		::glPopMatrix();
 	}
 
     if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) {
         // The supports are hidden in the editing mode, so it makes no sense to render the cuts.
-		::glPushMatrix();
-        ::glMultMatrixd(supports_trafo.data());
-        Eigen::Quaternionf q;
-		q.setFromTwoVectors(Vec3f::UnitZ(), up_supports);
-		Eigen::AngleAxisf aa(q);
-		::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
-		::glTranslatef(0.f, 0.f, 0.01f);
+        ::glPushMatrix();
         ::glColor3f(1.0f, 0.f, 0.37f);
         ::glBegin(GL_TRIANGLES);
-        for (const Vec2f& point : m_supports_clipper->get_triangles())
-            ::glVertex3f(point(0), point(1), height_supports);
-
+        for (const Vec3f& point : m_supports_clipper->get_triangles())
+            ::glVertex3f(point(0), point(1), point(2));
         ::glEnd();
 		::glPopMatrix();
 	}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index 130ac5202..515783d98 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -115,7 +115,6 @@ private:
 
     float m_clipping_plane_distance = 0.f;
     std::unique_ptr<ClippingPlane> m_clipping_plane;
-    std::unique_ptr<ClippingPlane> m_old_clipping_plane;
 
     // This map holds all translated description texts, so they can be easily referenced during layout calculations
     // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
diff --git a/src/slic3r/GUI/MeshClipper.cpp b/src/slic3r/GUI/MeshClipper.cpp
index c9648e7b6..21ddf52bc 100644
--- a/src/slic3r/GUI/MeshClipper.cpp
+++ b/src/slic3r/GUI/MeshClipper.cpp
@@ -18,7 +18,8 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh)
     if (m_mesh != &mesh) {
         m_mesh = &mesh;
         m_triangles_valid = false;
-        m_triangles.resize(0);
+        m_triangles2d.resize(0);
+        m_triangles3d.resize(0);
         m_tms.reset(nullptr);
     }
 }
@@ -28,17 +29,19 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
     if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) {
         m_trafo = trafo;
         m_triangles_valid = false;
-        m_triangles.resize(0);
+        m_triangles2d.resize(0);
+        m_triangles3d.resize(0);
     }
 }
 
 
-const std::vector<Vec2f>& MeshClipper::get_triangles()
+
+const std::vector<Vec3f>& MeshClipper::get_triangles()
 {
     if (! m_triangles_valid)
         recalculate_triangles();
 
-    return m_triangles;
+    return m_triangles3d;
 }
 
 void MeshClipper::recalculate_triangles()
@@ -49,34 +52,37 @@ void MeshClipper::recalculate_triangles()
     }
 
 
-    auto up_and_height = get_mesh_cut_normal();
-    Vec3f up = up_and_height.first;
-    float height_mesh = up_and_height.second;
+    const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
+    const Vec3f& scaling = m_trafo.get_scaling_factor().cast<float>();
+    // Calculate clipping plane normal in mesh coordinates.
+    Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast<float>();
+    Vec3f up (up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
+    // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
+    float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
 
+    // Now do the cutting
     std::vector<ExPolygons> list_of_expolys;
     m_tms->set_up_direction(up);
     m_tms->slice(std::vector<float>{height_mesh}, 0.f, &list_of_expolys, [](){});
-    m_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
+    m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.);
+
+    // Rotate the cut into world coords:
+    Eigen::Quaternionf q;
+    q.setFromTwoVectors(Vec3f::UnitZ(), up);
+    Transform3f tr = Transform3f::Identity();
+    tr.rotate(q);
+    tr = m_trafo.get_matrix().cast<float>() * tr;
+
+    m_triangles3d.clear();
+    m_triangles3d.reserve(m_triangles2d.size());
+    for (const Vec2f& pt : m_triangles2d) {
+        m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f));
+        m_triangles3d.back() = tr * m_triangles3d.back();
+    }
 
     m_triangles_valid = true;
 }
 
-std::pair<Vec3f, float> MeshClipper::get_mesh_cut_normal() const
-{
-    Transform3f instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
-    Vec3f scaling = m_trafo.get_scaling_factor().cast<float>();
-
-    // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
-    Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast<float>();
-    Vec3f up (up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
-
-    float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
-
-
-    return std::make_pair(up, height_mesh);
-}
-
-
 
 
 } // namespace GUI
diff --git a/src/slic3r/GUI/MeshClipper.hpp b/src/slic3r/GUI/MeshClipper.hpp
index dbeef4408..18b60761e 100644
--- a/src/slic3r/GUI/MeshClipper.hpp
+++ b/src/slic3r/GUI/MeshClipper.hpp
@@ -13,17 +13,16 @@ public:
     void set_mesh(const TriangleMesh& mesh);
     void set_transformation(const Geometry::Transformation& trafo);
 
-    const std::vector<Vec2f>& get_triangles();
+    const std::vector<Vec3f>& get_triangles();
 
 private:
     void recalculate_triangles();
-    std::pair<Vec3f, float> get_mesh_cut_normal() const;
-
 
     Geometry::Transformation m_trafo;
     const TriangleMesh* m_mesh = nullptr;
     ClippingPlane m_plane;
-    std::vector<Vec2f> m_triangles;
+    std::vector<Vec2f> m_triangles2d;
+    std::vector<Vec3f> m_triangles3d;
     bool m_triangles_valid = false;
     std::unique_ptr<TriangleMeshSlicer> m_tms;
 };

From 70c0c8759805472d1c72bf9d2254900fff44a01e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 13 Sep 2019 11:30:50 +0200
Subject: [PATCH 09/10] Renamed MeshClipper.cpp/.hpp to MeshUtils.cpp/.hpp More
 helper classes like the MeshClipper could live here Moved ClippingPlane class
 in here to start

---
 src/slic3r/CMakeLists.txt                     |  4 +-
 src/slic3r/GUI/GLCanvas3D.hpp                 | 49 +---------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp  |  4 +-
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp  |  3 +-
 src/slic3r/GUI/Gizmos/GLGizmos.hpp            |  8 +-
 src/slic3r/GUI/MeshClipper.hpp                | 36 -------
 .../GUI/{MeshClipper.cpp => MeshUtils.cpp}    | 12 ++-
 src/slic3r/GUI/MeshUtils.hpp                  | 94 +++++++++++++++++++
 8 files changed, 116 insertions(+), 94 deletions(-)
 delete mode 100644 src/slic3r/GUI/MeshClipper.hpp
 rename src/slic3r/GUI/{MeshClipper.cpp => MeshUtils.cpp} (97%)
 create mode 100644 src/slic3r/GUI/MeshUtils.hpp

diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 8587f01f2..17b76e629 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -85,8 +85,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/GUI_ObjectLayers.hpp
     GUI/LambdaObjectDialog.cpp
     GUI/LambdaObjectDialog.hpp
-    GUI/MeshClipper.cpp
-    GUI/MeshClipper.hpp
+    GUI/MeshUtils.cpp
+    GUI/MeshUtils.hpp
     GUI/Tab.cpp
     GUI/Tab.hpp
     GUI/ConfigManipulation.cpp
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 1b998c67e..b15402a52 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -12,6 +12,7 @@
 #include "Selection.hpp"
 #include "Gizmos/GLGizmosManager.hpp"
 #include "GUI_ObjectLayers.hpp"
+#include "MeshUtils.hpp"
 
 #include <float.h>
 
@@ -67,54 +68,6 @@ public:
 };
 
 
-class ClippingPlane
-{
-    double m_data[4];
-
-public:
-    ClippingPlane()
-    {
-        m_data[0] = 0.0;
-        m_data[1] = 0.0;
-        m_data[2] = 1.0;
-        m_data[3] = 0.0;
-    }
-
-    ClippingPlane(const Vec3d& direction, double offset)
-    {
-        Vec3d norm_dir = direction.normalized();
-        m_data[0] = norm_dir(0);
-        m_data[1] = norm_dir(1);
-        m_data[2] = norm_dir(2);
-        m_data[3] = offset;
-    }
-
-    bool operator==(const ClippingPlane& cp) const {
-        return m_data[0]==cp.m_data[0] && m_data[1]==cp.m_data[1] && m_data[2]==cp.m_data[2] && m_data[3]==cp.m_data[3];
-    }
-    bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); }
-
-    double distance(const Vec3d& pt) const {
-        assert(is_approx(get_normal().norm(), 1.));
-        return (-get_normal().dot(pt) + m_data[3]);
-    }
-
-    void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
-    void set_offset(double offset) { m_data[3] = offset; }
-    Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
-    bool is_active() const { return m_data[3] != DBL_MAX; }
-    static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
-    const double* get_data() const { return m_data; }
-
-    // Serialization through cereal library
-    template <class Archive>
-    void serialize( Archive & ar )
-    {
-        ar( m_data[0], m_data[1], m_data[2], m_data[3] );
-    }
-};
-
-
 wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
 
 using Vec2dEvent = Event<Vec2d>;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 99c4612ce..80afb29b1 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -1,6 +1,7 @@
 // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
 #include "GLGizmoSlaSupports.hpp"
 #include "slic3r/GUI/GLCanvas3D.hpp"
+#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
 
 #include <GL/glew.h>
 
@@ -12,11 +13,10 @@
 #include "slic3r/GUI/GUI.hpp"
 #include "slic3r/GUI/GUI_ObjectSettings.hpp"
 #include "slic3r/GUI/GUI_ObjectList.hpp"
-#include "slic3r/GUI/MeshClipper.hpp"
+#include "slic3r/GUI/MeshUtils.hpp"
 #include "slic3r/GUI/Plater.hpp"
 #include "slic3r/GUI/PresetBundle.hpp"
 #include "libslic3r/SLAPrint.hpp"
-#include "libslic3r/Tesselate.hpp"
 
 
 namespace Slic3r {
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index 515783d98..7bef33e1b 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -2,8 +2,6 @@
 #define slic3r_GLGizmoSlaSupports_hpp_
 
 #include "GLGizmoBase.hpp"
-#include "GLGizmos.hpp"
-#include "slic3r/GUI/GLSelectionRectangle.hpp"
 #include "slic3r/GUI/GLSelectionRectangle.hpp"
 
 // There is an L function in igl that would be overridden by our localization macro - let's undefine it...
@@ -22,6 +20,7 @@ namespace GUI {
 
 class ClippingPlane;
 class MeshClipper;
+enum class SLAGizmoEventType : unsigned char;
 
 class GLGizmoSlaSupports : public GLGizmoBase
 {
diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp
index 2e98899be..272fa098a 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp
@@ -2,7 +2,10 @@
 #define slic3r_GLGizmos_hpp_
 
 // this describes events being passed from GLCanvas3D to SlaSupport gizmo
-enum class SLAGizmoEventType {
+namespace Slic3r {
+namespace GUI {
+
+enum class SLAGizmoEventType : unsigned char {
     LeftDown = 1,
     LeftUp,
     RightDown,
@@ -20,6 +23,9 @@ enum class SLAGizmoEventType {
     ResetClippingPlane
 };
 
+} // namespace GUI
+} // namespace Slic3r
+
 #include "slic3r/GUI/Gizmos/GLGizmoMove.hpp"
 #include "slic3r/GUI/Gizmos/GLGizmoScale.hpp"
 #include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp"
diff --git a/src/slic3r/GUI/MeshClipper.hpp b/src/slic3r/GUI/MeshClipper.hpp
deleted file mode 100644
index 18b60761e..000000000
--- a/src/slic3r/GUI/MeshClipper.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef slic3r_MeshClipper_hpp_
-#define slic3r_MeshClipper_hpp_
-
-#include "libslic3r/Point.hpp"
-#include "slic3r/GUI/GLCanvas3D.hpp"
-
-namespace Slic3r {
-namespace GUI {
-
-class MeshClipper {
-public:
-    void set_plane(const ClippingPlane& plane);
-    void set_mesh(const TriangleMesh& mesh);
-    void set_transformation(const Geometry::Transformation& trafo);
-
-    const std::vector<Vec3f>& get_triangles();
-
-private:
-    void recalculate_triangles();
-
-    Geometry::Transformation m_trafo;
-    const TriangleMesh* m_mesh = nullptr;
-    ClippingPlane m_plane;
-    std::vector<Vec2f> m_triangles2d;
-    std::vector<Vec3f> m_triangles3d;
-    bool m_triangles_valid = false;
-    std::unique_ptr<TriangleMeshSlicer> m_tms;
-};
-
-
-    
-} // namespace GUI
-} // namespace Slic3r
-
-
-#endif // slic3r_MeshClipper_hpp_
diff --git a/src/slic3r/GUI/MeshClipper.cpp b/src/slic3r/GUI/MeshUtils.cpp
similarity index 97%
rename from src/slic3r/GUI/MeshClipper.cpp
rename to src/slic3r/GUI/MeshUtils.cpp
index 21ddf52bc..9542f0b1f 100644
--- a/src/slic3r/GUI/MeshClipper.cpp
+++ b/src/slic3r/GUI/MeshUtils.cpp
@@ -1,6 +1,7 @@
-#include "MeshClipper.hpp"
-#include "GLCanvas3D.hpp"
+#include "MeshUtils.hpp"
+
 #include "libslic3r/Tesselate.hpp"
+#include "libslic3r/TriangleMesh.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -13,6 +14,8 @@ void MeshClipper::set_plane(const ClippingPlane& plane)
     }
 }
 
+
+
 void MeshClipper::set_mesh(const TriangleMesh& mesh)
 {
     if (m_mesh != &mesh) {
@@ -24,6 +27,8 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh)
     }
 }
 
+
+
 void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
 {
     if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) {
@@ -44,6 +49,8 @@ const std::vector<Vec3f>& MeshClipper::get_triangles()
     return m_triangles3d;
 }
 
+
+
 void MeshClipper::recalculate_triangles()
 {
     if (! m_tms) {
@@ -51,7 +58,6 @@ void MeshClipper::recalculate_triangles()
         m_tms->init(m_mesh, [](){});
     }
 
-
     const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
     const Vec3f& scaling = m_trafo.get_scaling_factor().cast<float>();
     // Calculate clipping plane normal in mesh coordinates.
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
new file mode 100644
index 000000000..f97003a91
--- /dev/null
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -0,0 +1,94 @@
+#ifndef slic3r_MeshUtils_hpp_
+#define slic3r_MeshUtils_hpp_
+
+#include "libslic3r/Point.hpp"
+#include "libslic3r/Geometry.hpp"
+
+
+#include <cfloat>
+
+namespace Slic3r {
+
+class TriangleMesh;
+class TriangleMeshSlicer;
+
+namespace GUI {
+
+
+
+class ClippingPlane
+{
+    double m_data[4];
+
+public:
+    ClippingPlane()
+    {
+        m_data[0] = 0.0;
+        m_data[1] = 0.0;
+        m_data[2] = 1.0;
+        m_data[3] = 0.0;
+    }
+
+    ClippingPlane(const Vec3d& direction, double offset)
+    {
+        Vec3d norm_dir = direction.normalized();
+        m_data[0] = norm_dir(0);
+        m_data[1] = norm_dir(1);
+        m_data[2] = norm_dir(2);
+        m_data[3] = offset;
+    }
+
+    bool operator==(const ClippingPlane& cp) const {
+        return m_data[0]==cp.m_data[0] && m_data[1]==cp.m_data[1] && m_data[2]==cp.m_data[2] && m_data[3]==cp.m_data[3];
+    }
+    bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); }
+
+    double distance(const Vec3d& pt) const {
+        assert(is_approx(get_normal().norm(), 1.));
+        return (-get_normal().dot(pt) + m_data[3]);
+    }
+
+    void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
+    void set_offset(double offset) { m_data[3] = offset; }
+    Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
+    bool is_active() const { return m_data[3] != DBL_MAX; }
+    static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
+    const double* get_data() const { return m_data; }
+
+    // Serialization through cereal library
+    template <class Archive>
+    void serialize( Archive & ar )
+    {
+        ar( m_data[0], m_data[1], m_data[2], m_data[3] );
+    }
+};
+
+
+
+class MeshClipper {
+public:
+    void set_plane(const ClippingPlane& plane);
+    void set_mesh(const TriangleMesh& mesh);
+    void set_transformation(const Geometry::Transformation& trafo);
+
+    const std::vector<Vec3f>& get_triangles();
+
+private:
+    void recalculate_triangles();
+
+    Geometry::Transformation m_trafo;
+    const TriangleMesh* m_mesh = nullptr;
+    ClippingPlane m_plane;
+    std::vector<Vec2f> m_triangles2d;
+    std::vector<Vec3f> m_triangles3d;
+    bool m_triangles_valid = false;
+    std::unique_ptr<TriangleMeshSlicer> m_tms;
+};
+
+
+    
+} // namespace GUI
+} // namespace Slic3r
+
+
+#endif // slic3r_MeshUtils_hpp_

From ea8b6262cf21c0277b6aabf4d63983fca6ce6fe2 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 13 Sep 2019 16:16:37 +0200
Subject: [PATCH 10/10] Introduction of ClipperLib_Z: The Clipper library
 compiled with support of the Z coordinate, compiled in the ClipperLib_Z
 namespace.

Update of Lukas's new brim clipping:
All the brim contours are now clipped by the ClipperLib_Z library
in one shot.
---
 src/clipper/CMakeLists.txt  |   2 +
 src/clipper/clipper.cpp     |   6 +-
 src/clipper/clipper.hpp     |   7 +-
 src/clipper/clipper_z.cpp   |   7 ++
 src/clipper/clipper_z.hpp   |  18 +++++
 src/libslic3r/Print.cpp     | 152 +++++++++++++++++++++++++-----------
 src/libslic3r/pchheader.hpp |   2 +
 7 files changed, 148 insertions(+), 46 deletions(-)
 create mode 100644 src/clipper/clipper_z.cpp
 create mode 100644 src/clipper/clipper_z.hpp

diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt
index d6f3861dc..412ab53c7 100644
--- a/src/clipper/CMakeLists.txt
+++ b/src/clipper/CMakeLists.txt
@@ -4,4 +4,6 @@ cmake_minimum_required(VERSION 2.6)
 add_library(clipper STATIC
     clipper.cpp
     clipper.hpp
+    clipper_z.cpp
+    clipper_z.hpp
 )
diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp
index 228e0c6ef..b85cf9025 100644
--- a/src/clipper/clipper.cpp
+++ b/src/clipper/clipper.cpp
@@ -51,7 +51,11 @@
 #include <Shiny/Shiny.h>
 #include <libslic3r/Int128.hpp>
 
+#ifdef use_xyz
+namespace ClipperLib_Z {
+#else /* use_xyz */
 namespace ClipperLib {
+#endif /* use_xyz */
 
 static double const pi = 3.141592653589793238;
 static double const two_pi = pi *2;
@@ -1616,7 +1620,7 @@ void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)
   else if (pt == e1.Top) pt.Z = e1.Top.Z;
   else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
   else if (pt == e2.Top) pt.Z = e2.Top.Z;
-  else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); 
+  else m_ZFill(e1.Bot, e1.Top, e2.Bot, e2.Top, pt);
 }
 //------------------------------------------------------------------------------
 #endif
diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp
index 8a28fe46f..8b34779e3 100644
--- a/src/clipper/clipper.hpp
+++ b/src/clipper/clipper.hpp
@@ -35,6 +35,7 @@
 #define clipper_hpp
 
 #include <inttypes.h>
+#include <functional>
 
 #define CLIPPER_VERSION "6.2.6"
 
@@ -56,7 +57,11 @@
 #include <functional>
 #include <queue>
 
+#ifdef use_xyz
+namespace ClipperLib_Z {
+#else /* use_xyz */
 namespace ClipperLib {
+#endif /* use_xyz */
 
 enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
 enum PolyType { ptSubject, ptClip };
@@ -114,7 +119,7 @@ struct DoublePoint
 //------------------------------------------------------------------------------
 
 #ifdef use_xyz
-typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
+typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback;
 #endif
 
 enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
diff --git a/src/clipper/clipper_z.cpp b/src/clipper/clipper_z.cpp
new file mode 100644
index 000000000..4a54ef346
--- /dev/null
+++ b/src/clipper/clipper_z.cpp
@@ -0,0 +1,7 @@
+// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
+
+// Enable the Z coordinate support.
+#define use_xyz
+
+// and let it compile
+#include "clipper.cpp"
diff --git a/src/clipper/clipper_z.hpp b/src/clipper/clipper_z.hpp
new file mode 100644
index 000000000..0f31ac11c
--- /dev/null
+++ b/src/clipper/clipper_z.hpp
@@ -0,0 +1,18 @@
+// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
+
+#ifndef clipper_z_hpp
+#ifdef clipper_hpp
+#error "You should include the clipper_z.hpp first"
+#endif
+
+#define clipper_z_hpp
+
+// Enable the Z coordinate support.
+#define use_xyz
+
+#include "clipper.hpp"
+
+#undef clipper_hpp
+#undef use_xyz
+
+#endif clipper_z_hpp
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index c22478d84..a5137b3e4 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1,3 +1,5 @@
+#include "clipper/clipper_z.hpp"
+
 #include "Print.hpp"
 #include "BoundingBox.hpp"
 #include "ClipperUtils.hpp"
@@ -1729,53 +1731,115 @@ void Print::_make_brim()
                                               ClipperLib::jtRound,
                                               float(scale_(0.1)));
 
-        const Polygon& skirt_inner = skirt_inners.front();
-        const Polygon& skirt_outer = skirt_outers.front();
+        // First calculate the trimming region.
+		ClipperLib_Z::Paths trimming;
+		{
+		    ClipperLib_Z::Paths input_subject;
+		    ClipperLib_Z::Paths input_clip;
+		    for (const Polygon &poly : skirt_outers) {
+		    	input_subject.emplace_back();
+		    	ClipperLib_Z::Path &out = input_subject.back();
+		    	out.reserve(poly.points.size());
+			    for (const Point &pt : poly.points)
+					out.emplace_back(pt.x(), pt.y(), 0);
+		    }
+		    for (const Polygon &poly : skirt_inners) {
+		    	input_clip.emplace_back();
+		    	ClipperLib_Z::Path &out = input_clip.back();
+		    	out.reserve(poly.points.size());
+			    for (const Point &pt : poly.points)
+					out.emplace_back(pt.x(), pt.y(), 0);
+		    }
+		    // init Clipper
+		    ClipperLib_Z::Clipper clipper;	    
+		    // add polygons
+		    clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
+		    clipper.AddPaths(input_clip,    ClipperLib_Z::ptClip,    true);
+		    // perform operation
+		    clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
+		}
 
-        for (size_t i=0; i<loops.size(); ++i) {
-            Polylines lines;
-            lines.push_back(Polyline());
-            bool del = false;
-            const Polygon& poly = loops[i];
-            // Check all points of the polygon, consider the first to also be at the end so the loop is closed
-            for (int pt_idx=0; pt_idx <= (int)poly.points.size(); ++pt_idx) {
-                const Point* pt = (pt_idx == (int)poly.points.size() ? &poly.points[0] : &poly.points[pt_idx]);
-                const Point* last_pt = (pt_idx == 0 ? nullptr : &poly.points[pt_idx-1]);
-                bool valid_point = skirt_inner.contains(*pt) || ! skirt_outer.contains(*pt);
-                Points intersections(2); // inner and outer intersection
+		// Second, trim the extrusion loops with the trimming regions.
+		ClipperLib_Z::Paths loops_trimmed;
+		{
+			// Produce a closed polyline (repeat the first point at the end).
+			ClipperLib_Z::Paths input_clip;
+			for (const Polygon &loop : loops) {
+				input_clip.emplace_back();
+				ClipperLib_Z::Path& out = input_clip.back();
+				out.reserve(loop.points.size());
+				int64_t loop_idx = &loop - &loops.front();
+				for (const Point& pt : loop.points)
+					// The Z coordinate carries index of the source loop.
+					out.emplace_back(pt.x(), pt.y(), loop_idx + 1);
+				out.emplace_back(out.front());
+			}
+			// init Clipper
+			ClipperLib_Z::Clipper clipper;
+			clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
+				// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
+				// hat the Z coordinate not set to the contour coordinate.
+				pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z));
+			});
+			// add polygons
+			clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
+			clipper.AddPaths(trimming,   ClipperLib_Z::ptClip,    true);
+			// perform operation
+			ClipperLib_Z::PolyTree loops_trimmed_tree;
+			clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
+			ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed);
+		}
 
-                if (pt_idx > 0) {
-                    Line line(*last_pt, *pt);
-                    bool inner = skirt_inner.first_intersection(line, &intersections[0]);
-                    bool outer = skirt_outer.first_intersection(line, &intersections[1]);
-                    del = del || inner || outer;
-                    if (inner != outer) {// there is exactly one intersection
-                        lines.back().append(inner ? intersections[0] : intersections[1]);
-                    }
-                    if (inner && outer) {
-                        int nearest_idx = pt->nearest_point_index(intersections);
-                        lines.back().append(intersections[! nearest_idx]);
-                        lines.push_back(Polyline());
-                        lines.back().append(intersections[nearest_idx]);
-                    }
-                }
-                if (valid_point)
-                    lines.back().append(*pt);
-                else {
-                    del = true;
-                    lines.push_back(Polyline());
-                }
-            }
-            // If we found a single intersection, we should erase the respective ExtrusionLoop
-            // and append the polylines that we created.
-            if (del) {
-                loops.erase(loops.begin() + (i--));
-                extrusion_entities_append_paths(m_brim.entities, std::move(lines), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
-            }
-        }
+		// Third, produce the extrusions, sorted by the source loop indices.
+		{
+			std::vector<std::pair<const ClipperLib_Z::Path*, size_t>> loops_trimmed_order;
+			loops_trimmed_order.reserve(loops_trimmed.size());
+			for (const ClipperLib_Z::Path &path : loops_trimmed) {
+				size_t input_idx = 0;
+				for (const ClipperLib_Z::IntPoint &pt : path)
+					if (pt.Z > 0) {
+						input_idx = (size_t)pt.Z;
+						break;
+					}
+				assert(input_idx != 0);
+				loops_trimmed_order.emplace_back(&path, input_idx);
+			}
+			std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(),
+				[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
+					return l.second < r.second;
+				});
+			Vec3f last_pt(0.f, 0.f, 0.f);
+
+			for (size_t i = 0; i < loops_trimmed_order.size();) {
+				// Find all pieces that the initial loop was split into.
+				size_t j = i + 1;
+				for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].first == loops_trimmed_order[j].first; ++ j) ;
+				const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
+				if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) {
+					auto *loop = new ExtrusionLoop();
+					m_brim.entities.emplace_back(loop);
+					loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
+		            Points &points = loop->paths.front().polyline.points;
+		            points.reserve(first_path.size());
+		            for (const ClipperLib_Z::IntPoint &pt : first_path)
+		            	points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
+		            i = j;
+				} else {
+			    	//FIXME this is not optimal as the G-code generator will follow the sequence of paths verbatim without respect to minimum travel distance.
+			    	for (; i < j; ++ i) {
+			            m_brim.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())));
+						const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
+			            Points &points = static_cast<ExtrusionPath*>(m_brim.entities.back())->polyline.points;
+			            points.reserve(path.size());
+			            for (const ClipperLib_Z::IntPoint &pt : path)
+			            	points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
+		           	}
+		        }
+			}
+		}
+    } else {
+    	extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
     }
-
-    extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
 }
 
 // Wipe tower support.
diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp
index c0ffe2108..67b3d3a21 100644
--- a/src/libslic3r/pchheader.hpp
+++ b/src/libslic3r/pchheader.hpp
@@ -105,6 +105,8 @@
 #include <cereal/access.hpp>
 #include <cereal/types/base_class.hpp>
 
+#include <clipper/clipper_z.hpp>
+#include <clipper/clipper.hpp>
 #include "BoundingBox.hpp"
 #include "ClipperUtils.hpp"
 #include "Config.hpp"