From 175b6fd9f512a4d5cf155a4cac813fe611582e25 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 9 Oct 2019 14:59:09 +0200
Subject: [PATCH 1/3] Fix concave hull having holes.

---
 src/libslic3r/CMakeLists.txt                  |   2 +
 src/libslic3r/SLA/ConcaveHull.cpp             | 171 ++++++++++++++
 src/libslic3r/SLA/ConcaveHull.hpp             |  53 +++++
 src/libslic3r/SLA/SLAPad.cpp                  | 209 ++----------------
 .../SLA/SLASupportTreeBuildsteps.hpp          |   2 +-
 tests/sla_print/sla_print_tests.cpp           |  56 ++++-
 6 files changed, 292 insertions(+), 201 deletions(-)
 create mode 100644 src/libslic3r/SLA/ConcaveHull.cpp
 create mode 100644 src/libslic3r/SLA/ConcaveHull.hpp

diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index fe8b0f71d..633549b42 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -194,6 +194,8 @@ add_library(libslic3r STATIC
     SLA/SLARaster.cpp
     SLA/SLARasterWriter.hpp
     SLA/SLARasterWriter.cpp
+    SLA/ConcaveHull.hpp
+    SLA/ConcaveHull.cpp
 )
 
 encoding_check(libslic3r)
diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp
new file mode 100644
index 000000000..dff061721
--- /dev/null
+++ b/src/libslic3r/SLA/ConcaveHull.cpp
@@ -0,0 +1,171 @@
+#include "ConcaveHull.hpp"
+#include <libslic3r/MTUtils.hpp>
+#include <libslic3r/ClipperUtils.hpp>
+#include "SLASpatIndex.hpp"
+#include <boost/log/trivial.hpp>
+
+namespace Slic3r {
+namespace sla {
+
+inline Vec3d to_vec3(const Vec2crd &v2) { return {double(v2(X)), double(v2(Y)), 0.}; }
+inline Vec3d to_vec3(const Vec2d &v2) { return {v2(X), v2(Y), 0.}; }
+inline Vec2crd to_vec2(const Vec3d &v3) { return {coord_t(v3(X)), coord_t(v3(Y))}; }
+
+Point ConcaveHull::centroid(const Points &pp)
+{
+    Point c;
+    switch(pp.size()) {
+    case 0: break;
+    case 1: c = pp.front(); break;
+    case 2: c = (pp[0] + pp[1]) / 2; break;
+    default: {
+        auto MAX = std::numeric_limits<Point::coord_type>::max();
+        auto MIN = std::numeric_limits<Point::coord_type>::min();
+        Point min = {MAX, MAX}, max = {MIN, MIN};
+
+        for(auto& p : pp) {
+            if(p(0) < min(0)) min(0) = p(0);
+            if(p(1) < min(1)) min(1) = p(1);
+            if(p(0) > max(0)) max(0) = p(0);
+            if(p(1) > max(1)) max(1) = p(1);
+        }
+        c(0) = min(0) + (max(0) - min(0)) / 2;
+        c(1) = min(1) + (max(1) - min(1)) / 2;
+        break;
+    }
+    }
+
+    return c;
+}
+
+// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
+// mode
+ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
+                              coord_t                  delta,
+                              ClipperLib::JoinType     jointype)
+{
+    using ClipperLib::ClipperOffset;
+    using ClipperLib::etClosedPolygon;
+    using ClipperLib::Paths;
+    using ClipperLib::Path;
+
+    ClipperOffset offs;
+    offs.ArcTolerance = scaled<double>(0.01);
+
+    for (auto &p : paths)
+        // If the input is not at least a triangle, we can not do this algorithm
+        if(p.size() < 3) {
+            BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
+            return {};
+        }
+
+    offs.AddPaths(paths, jointype, etClosedPolygon);
+
+    Paths result;
+    offs.Execute(result, static_cast<double>(delta));
+
+    return result;
+}
+
+Points ConcaveHull::calculate_centroids() const
+{
+    // We get the centroids of all the islands in the 2D slice
+    Points centroids = reserve_vector<Point>(m_polys.size());
+    std::transform(m_polys.begin(), m_polys.end(),
+                   std::back_inserter(centroids),
+                   [this](const Polygon &poly) { return centroid(poly); });
+
+    return centroids;
+}
+
+void ConcaveHull::merge_polygons() { m_polys = get_contours(union_ex(m_polys)); }
+
+void ConcaveHull::add_connector_rectangles(const Points &centroids,
+                                           coord_t       max_dist,
+                                           ThrowOnCancel thr)
+{
+    // Centroid of the centroids of islands. This is where the additional
+    // connector sticks are routed.
+    Point cc = centroid(centroids);
+
+    PointIndex ctrindex;
+    unsigned  idx = 0;
+    for(const Point &ct : centroids) ctrindex.insert(to_vec3(ct), idx++);
+
+    m_polys.reserve(m_polys.size() + centroids.size());
+
+    idx = 0;
+    for (const Point &c : centroids) {
+        thr();
+
+        double dx = c.x() - cc.x(), dy = c.y() - cc.y();
+        double l  = std::sqrt(dx * dx + dy * dy);
+        double nx = dx / l, ny = dy / l;
+
+        const Point &ct = centroids[idx];
+
+        std::vector<PointIndexEl> result = ctrindex.nearest(to_vec3(ct), 2);
+
+        double dist = max_dist;
+        for (const PointIndexEl &el : result)
+            if (el.second != idx) {
+                dist = Line(to_vec2(el.first), ct).length();
+                break;
+            }
+
+        idx++;
+
+        if (dist >= max_dist) return;
+
+        Polygon r;
+        r.points.reserve(3);
+        r.points.emplace_back(cc);
+
+        Point n(scaled(nx), scaled(ny));
+        r.points.emplace_back(c + Point(n.y(), -n.x()));
+        r.points.emplace_back(c + Point(-n.y(), n.x()));
+        offset(r, scaled<float>(1.));
+
+        m_polys.emplace_back(r);
+    }
+}
+
+ConcaveHull::ConcaveHull(const Polygons &polys, double mergedist, ThrowOnCancel thr)
+{
+    if(polys.empty()) return;
+
+    m_polys = polys;
+    merge_polygons();
+
+    if(m_polys.size() == 1) return;
+
+    Points centroids = calculate_centroids();
+
+    add_connector_rectangles(centroids, scaled(mergedist), thr);
+
+    merge_polygons();
+}
+
+ExPolygons ConcaveHull::to_expolygons() const
+{
+    auto ret = reserve_vector<ExPolygon>(m_polys.size());
+    for (const Polygon &p : m_polys) ret.emplace_back(ExPolygon(p));
+    return ret;
+}
+
+ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
+{
+    ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(hull.polygons());
+    paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound);
+    paths = fast_offset(paths, -delta, ClipperLib::jtRound);
+    ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(paths);
+    for (ExPolygon &p : ret) p.holes = {};
+    return ret;
+}
+
+Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta)
+{
+    return to_polygons(offset_waffle_style_ex(hull, delta));
+}
+
+}} // namespace Slic3r::sla
diff --git a/src/libslic3r/SLA/ConcaveHull.hpp b/src/libslic3r/SLA/ConcaveHull.hpp
new file mode 100644
index 000000000..94e16d77c
--- /dev/null
+++ b/src/libslic3r/SLA/ConcaveHull.hpp
@@ -0,0 +1,53 @@
+#ifndef CONCAVEHULL_HPP
+#define CONCAVEHULL_HPP
+
+#include <libslic3r/ExPolygon.hpp>
+
+namespace Slic3r {
+namespace sla {
+
+inline Polygons get_contours(const ExPolygons &poly)
+{
+    Polygons ret; ret.reserve(poly.size());
+    for (const ExPolygon &p : poly) ret.emplace_back(p.contour);
+
+    return ret;
+}
+
+using ThrowOnCancel = std::function<void()>;
+
+/// A fake concave hull that is constructed by connecting separate shapes
+/// with explicit bridges. Bridges are generated from each shape's centroid
+/// to the center of the "scene" which is the centroid calculated from the shape
+/// centroids (a star is created...)
+class ConcaveHull {
+    Polygons m_polys;
+
+    static Point centroid(const Points& pp);
+
+    static inline Point centroid(const Polygon &poly) { return poly.centroid(); }
+
+    Points calculate_centroids() const;
+
+    void merge_polygons();
+
+    void add_connector_rectangles(const Points &centroids,
+                                  coord_t       max_dist,
+                                  ThrowOnCancel thr);
+public:
+
+    ConcaveHull(const ExPolygons& polys, double merge_dist, ThrowOnCancel thr)
+        : ConcaveHull{to_polygons(polys), merge_dist, thr} {}
+
+    ConcaveHull(const Polygons& polys, double mergedist, ThrowOnCancel thr);
+
+    const Polygons & polygons() const { return m_polys; }
+
+    ExPolygons to_expolygons() const;
+};
+
+ExPolygons offset_waffle_style_ex(const ConcaveHull &ccvhull, coord_t delta);
+Polygons   offset_waffle_style(const ConcaveHull &polys, coord_t delta);
+
+}}     // namespace Slic3r::sla
+#endif // CONCAVEHULL_HPP
diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/SLAPad.cpp
index 71f8b1c7f..7cd9eb4e4 100644
--- a/src/libslic3r/SLA/SLAPad.cpp
+++ b/src/libslic3r/SLA/SLAPad.cpp
@@ -1,6 +1,7 @@
 #include "SLAPad.hpp"
 #include "SLABoilerPlate.hpp"
 #include "SLASpatIndex.hpp"
+#include "ConcaveHull.hpp"
 
 #include "boost/log/trivial.hpp"
 #include "SLABoostAdapter.hpp"
@@ -206,36 +207,6 @@ Contour3D inline straight_walls(const Polygon &plate,
     return walls(plate, plate, lo_z, hi_z, .0 /*offset_diff*/, thr);
 }
 
-// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
-// mode
-ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
-                              coord_t                  delta,
-                              ClipperLib::JoinType     jointype)
-{
-    using ClipperLib::ClipperOffset;
-    using ClipperLib::etClosedPolygon;
-    using ClipperLib::Paths;
-    using ClipperLib::Path;
-
-    ClipperOffset offs;
-    offs.ArcTolerance = scaled<double>(0.01);
-
-    for (auto &p : paths)
-        // If the input is not at least a triangle, we can not do this algorithm
-        if(p.size() < 3) {
-            BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
-            return {};
-        }
-
-    offs.AddPaths(paths, jointype, etClosedPolygon);
-
-    Paths result;
-    offs.Execute(result, static_cast<double>(delta));
-
-    return result;
-}
-
-
 // Function to cut tiny connector cavities for a given polygon. The input poly
 // will be offsetted by "padding" and small rectangle shaped cavities will be
 // inserted along the perimeter in every "stride" distance. The stick rectangles
@@ -322,158 +293,15 @@ ExPolygons breakstick_holes(const ExPolygons &input, Args...args)
     return ret;
 }
 
-/// A fake concave hull that is constructed by connecting separate shapes
-/// with explicit bridges. Bridges are generated from each shape's centroid
-/// to the center of the "scene" which is the centroid calculated from the shape
-/// centroids (a star is created...)
-class ConcaveHull {
-    Polygons m_polys;
+static inline coord_t get_waffle_offset(const PadConfig &c)
+{
+    return scaled(c.brim_size_mm + c.wing_distance());
+}
 
-    Point centroid(const Points& pp) const
-    {
-        Point c;
-        switch(pp.size()) {
-        case 0: break;
-        case 1: c = pp.front(); break;
-        case 2: c = (pp[0] + pp[1]) / 2; break;
-        default: {
-            auto MAX = std::numeric_limits<Point::coord_type>::max();
-            auto MIN = std::numeric_limits<Point::coord_type>::min();
-            Point min = {MAX, MAX}, max = {MIN, MIN};
-
-            for(auto& p : pp) {
-                if(p(0) < min(0)) min(0) = p(0);
-                if(p(1) < min(1)) min(1) = p(1);
-                if(p(0) > max(0)) max(0) = p(0);
-                if(p(1) > max(1)) max(1) = p(1);
-            }
-            c(0) = min(0) + (max(0) - min(0)) / 2;
-            c(1) = min(1) + (max(1) - min(1)) / 2;
-            break;
-        }
-        }
-
-        return c;
-    }
-
-    inline Point centroid(const Polygon &poly) const { return poly.centroid(); }
-
-    Points calculate_centroids() const
-    {
-        // We get the centroids of all the islands in the 2D slice
-        Points centroids = reserve_vector<Point>(m_polys.size());
-        std::transform(m_polys.begin(), m_polys.end(),
-                       std::back_inserter(centroids),
-                       [this](const Polygon &poly) { return centroid(poly); });
-
-        return centroids;
-    }
-
-    void merge_polygons() { m_polys = union_(m_polys); }
-
-    void add_connector_rectangles(const Points &centroids,
-                                  coord_t       max_dist,
-                                  ThrowOnCancel thr)
-    {
-        namespace bgi = boost::geometry::index;
-        using PointIndexElement = std::pair<Point, unsigned>;
-        using PointIndex = bgi::rtree<PointIndexElement, bgi::rstar<16, 4>>;
-
-        // Centroid of the centroids of islands. This is where the additional
-        // connector sticks are routed.
-        Point cc = centroid(centroids);
-
-        PointIndex ctrindex;
-        unsigned  idx = 0;
-        for(const Point &ct : centroids)
-            ctrindex.insert(std::make_pair(ct, idx++));
-
-        m_polys.reserve(m_polys.size() + centroids.size());
-
-        idx = 0;
-        for (const Point &c : centroids) {
-            thr();
-
-            double dx = c.x() - cc.x(), dy = c.y() - cc.y();
-            double l  = std::sqrt(dx * dx + dy * dy);
-            double nx = dx / l, ny = dy / l;
-
-            const Point &ct = centroids[idx];
-
-            std::vector<PointIndexElement> result;
-            ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result));
-
-            double dist = max_dist;
-            for (const PointIndexElement &el : result)
-                if (el.second != idx) {
-                    dist = Line(el.first, ct).length();
-                    break;
-                }
-
-            idx++;
-
-            if (dist >= max_dist) return;
-
-            Polygon r;
-            r.points.reserve(3);
-            r.points.emplace_back(cc);
-
-            Point d(scaled(nx), scaled(ny));
-            r.points.emplace_back(c + Point(-d.y(), d.x()));
-            r.points.emplace_back(c + Point(d.y(), -d.x()));
-            offset(r, scaled<float>(1.));
-
-            m_polys.emplace_back(r);
-        }
-    }
-
-public:
-
-    ConcaveHull(const ExPolygons& polys, double merge_dist, ThrowOnCancel thr)
-        : ConcaveHull{to_polygons(polys), merge_dist, thr} {}
-
-    ConcaveHull(const Polygons& polys, double mergedist, ThrowOnCancel thr)
-    {
-        if(polys.empty()) return;
-
-        m_polys = polys;
-        merge_polygons();
-
-        if(m_polys.size() == 1) return;
-
-        Points centroids = calculate_centroids();
-
-        add_connector_rectangles(centroids, scaled(mergedist), thr);
-
-        merge_polygons();
-    }
-
-    // const Polygons & polygons() const { return m_polys; }
-
-    ExPolygons to_expolygons() const
-    {
-        auto ret = reserve_vector<ExPolygon>(m_polys.size());
-        for (const Polygon &p : m_polys) ret.emplace_back(ExPolygon(p));
-        return ret;
-    }
-
-    void offset_waffle_style(coord_t delta) {
-        ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(m_polys);
-        paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound);
-        paths = fast_offset(paths, -delta, ClipperLib::jtRound);
-        m_polys = ClipperPaths_to_Slic3rPolygons(paths);
-    }
-
-    static inline coord_t get_waffle_offset(const PadConfig &c)
-    {
-        return scaled(c.brim_size_mm + c.wing_distance());
-    }
-
-    static inline double get_merge_distance(const PadConfig &c)
-    {
-        return 2. * (1.8 * c.wall_thickness_mm) + c.max_merge_dist_mm;
-    }
-};
+static inline double get_merge_distance(const PadConfig &c)
+{
+    return 2. * (1.8 * c.wall_thickness_mm) + c.max_merge_dist_mm;
+}
 
 // Part of the pad configuration that is used for 3D geometry generation
 struct PadConfig3D {
@@ -591,7 +419,7 @@ public:
                       scaled<float>(cfg.embed_object.object_gap_mm),
                       ClipperLib::jtMiter, 1);
 
-        ConcaveHull fullcvh =
+        ExPolygons fullcvh =
             wafflized_concave_hull(support_blueprint, model_bp_offs, cfg, thr);
 
         auto model_bp_sticks =
@@ -600,7 +428,7 @@ public:
                              cfg.embed_object.stick_width_mm,
                              cfg.embed_object.stick_penetration_mm);
 
-        ExPolygons fullpad = diff_ex(fullcvh.to_expolygons(), model_bp_sticks);
+        ExPolygons fullpad = diff_ex(fullcvh, model_bp_sticks);
 
         remove_redundant_parts(fullpad);
 
@@ -619,7 +447,7 @@ private:
 
     // Create the wafflized pad around all object in the scene. This pad doesnt
     // have any holes yet.
-    ConcaveHull wafflized_concave_hull(const ExPolygons &supp_bp,
+    ExPolygons wafflized_concave_hull(const ExPolygons &supp_bp,
                                        const ExPolygons &model_bp,
                                        const PadConfig  &cfg,
                                        ThrowOnCancel     thr)
@@ -629,10 +457,8 @@ private:
         for (auto &ep : supp_bp) allin.emplace_back(ep.contour);
         for (auto &ep : model_bp) allin.emplace_back(ep.contour);
 
-        ConcaveHull ret{allin, ConcaveHull::get_merge_distance(cfg), thr};
-        ret.offset_waffle_style(ConcaveHull::get_waffle_offset(cfg));
-
-        return ret;
+        ConcaveHull cchull{allin, get_merge_distance(cfg), thr};
+        return offset_waffle_style_ex(cchull, get_waffle_offset(cfg));
     }
 
     // To remove parts of the pad skeleton which do not host any supports
@@ -663,10 +489,9 @@ public:
         for (auto &ep : support_blueprint) outer.emplace_back(ep.contour);
         for (auto &ep : model_blueprint) outer.emplace_back(ep.contour);
 
-        ConcaveHull ochull{outer, ConcaveHull::get_merge_distance(cfg), thr};
+        ConcaveHull ochull{outer, get_merge_distance(cfg), thr};
 
-        ochull.offset_waffle_style(ConcaveHull::get_waffle_offset(cfg));
-        outer = ochull.to_expolygons();
+        outer = offset_waffle_style_ex(ochull, get_waffle_offset(cfg));
     }
 };
 
@@ -861,7 +686,7 @@ std::string PadConfig::validate() const
 
     if (brim_size_mm < MIN_BRIM_SIZE_MM ||
         bottom_offset() > brim_size_mm + wing_distance() ||
-        ConcaveHull::get_waffle_offset(*this) <= MIN_BRIM_SIZE_MM)
+        get_waffle_offset(*this) <= MIN_BRIM_SIZE_MM)
         return L("Pad brim size is too small for the current configuration.");
 
     return "";
diff --git a/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp
index b92e44dbd..e953cc136 100644
--- a/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp
+++ b/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp
@@ -175,7 +175,7 @@ class SupportTreeBuildsteps {
 
     // A spatial index to easily find strong pillars to connect to.
     PillarIndex m_pillar_index;
-    
+
     // When bridging heads to pillars... TODO: find a cleaner solution
     ccr::BlockingMutex m_bridge_mutex;
 
diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp
index 74b1ed8b1..f41fd3200 100644
--- a/tests/sla_print/sla_print_tests.cpp
+++ b/tests/sla_print/sla_print_tests.cpp
@@ -17,6 +17,7 @@
 #include "libslic3r/SLA/SLASupportTreeBuildsteps.hpp"
 #include "libslic3r/SLA/SLAAutoSupports.hpp"
 #include "libslic3r/SLA/SLARaster.hpp"
+#include "libslic3r/SLA/ConcaveHull.hpp"
 #include "libslic3r/MTUtils.hpp"
 
 #include "libslic3r/SVG.hpp"
@@ -79,6 +80,43 @@ struct PadByproducts
     TriangleMesh mesh;
 };
 
+void _test_concave_hull(const Polygons &hull, const ExPolygons &polys)
+{
+    REQUIRE(polys.size() >=hull.size());
+
+    double polys_area = 0;
+    for (const ExPolygon &p : polys) polys_area += p.area();
+
+    double cchull_area = 0;
+    for (const Slic3r::Polygon &p : hull) cchull_area += p.area();
+
+    REQUIRE(cchull_area >= Approx(polys_area));
+
+    size_t cchull_holes = 0;
+    for (const Slic3r::Polygon &p : hull)
+        cchull_holes += p.is_clockwise() ? 1 : 0;
+
+    REQUIRE(cchull_holes == 0);
+
+    Polygons intr = diff(to_polygons(polys), hull);
+    REQUIRE(intr.empty());
+}
+
+void test_concave_hull(const ExPolygons &polys) {
+    sla::PadConfig pcfg;
+
+    Slic3r::sla::ConcaveHull cchull{polys, pcfg.max_merge_dist_mm, []{}};
+
+    _test_concave_hull(cchull.polygons(), polys);
+
+    coord_t delta = scaled(pcfg.brim_size_mm + pcfg.wing_distance());
+    ExPolygons wafflex = sla::offset_waffle_style_ex(cchull, delta);
+    Polygons waffl = sla::offset_waffle_style(cchull, delta);
+
+    _test_concave_hull(to_polygons(wafflex), polys);
+    _test_concave_hull(waffl, polys);
+}
+
 void test_pad(const std::string &   obj_filename,
               const sla::PadConfig &padcfg,
               PadByproducts &       out)
@@ -92,6 +130,8 @@ void test_pad(const std::string &   obj_filename,
     // Create pad skeleton only from the model
     Slic3r::sla::pad_blueprint(mesh, out.model_contours);
 
+    test_concave_hull(out.model_contours);
+
     REQUIRE_FALSE(out.model_contours.empty());
 
     // Create the pad geometry for the model contours only
@@ -257,7 +297,7 @@ void export_failed_case(const std::vector<ExPolygons> &support_slices,
         const ExPolygons &sup_slice = support_slices[n];
         const ExPolygons &mod_slice = byproducts.model_slices[n];
         Polygons intersections = intersection(sup_slice, mod_slice);
-        
+
         std::stringstream ss;
         if (!intersections.empty()) {
             ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg";
@@ -268,7 +308,7 @@ void export_failed_case(const std::vector<ExPolygons> &support_slices,
             svg.Close();
         }
     }
-    
+
     TriangleMesh m;
     byproducts.supporttree.retrieve_full_mesh(m);
     m.merge(byproducts.input_mesh);
@@ -288,7 +328,7 @@ void test_support_model_collision(
     // Set head penetration to a small negative value which should ensure that
     // the supports will not touch the model body.
     supportcfg.head_penetration_mm = -0.15;
-    
+
     // TODO: currently, the tailheads penetrating into the model body do not
     // respect the penetration parameter properly. No issues were reported so
     // far but we should definitely fix this.
@@ -305,7 +345,7 @@ void test_support_model_collision(
     bool support_mesh_is_empty =
         byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() &&
         byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty();
-    
+
     if (support_mesh_is_empty)
         REQUIRE(support_slices.empty());
     else
@@ -320,7 +360,7 @@ void test_support_model_collision(
 
         notouch = notouch && intersections.empty();
     }
-    
+
     if (!notouch) export_failed_case(support_slices, byproducts);
 
     REQUIRE(notouch);
@@ -359,12 +399,12 @@ template <class I, class II> void test_pairhash()
 
     const I Ibits = int(sizeof(I) * CHAR_BIT);
     const II IIbits = int(sizeof(II) * CHAR_BIT);
-    
+
     int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits;
     if (std::is_signed<I>::value) bits -= 1;
-    const I Imin = std::is_signed<I>::value ? -I(std::pow(2., bits)) : 0;
+    const I Imin = 0;
     const I Imax = I(std::pow(2., bits) - 1);
-    
+
     std::uniform_int_distribution<I> dis(Imin, Imax);
 
     for (size_t i = 0; i < nums;) {

From a65b8f462dd46c8a5f1410504521f26621aeb882 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 10 Oct 2019 09:35:21 +0200
Subject: [PATCH 2/3] #3008 - Reduced max size of bed texture

---
 src/slic3r/GUI/GLCanvas3DManager.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp
index 5fbefcc6e..9690e8a8d 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -107,7 +107,9 @@ void GLCanvas3DManager::GLInfo::detect() const
         m_renderer = data;
 
     glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size));
-    
+
+    m_max_tex_size /= 2;
+
     if (GLEW_EXT_texture_filter_anisotropic)
         glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy));
 

From 7b45014721eb9b509516bbfe885e311edff8c264 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 10 Oct 2019 09:52:13 +0200
Subject: [PATCH 3/3] GCode.cpp: Removed a failing assert that was no longer
 needed The situation it checked happens when the wipe tower is lower than the
 tallest print object. The function processes that correctly.

---
 src/libslic3r/GCode.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 65264c9cd..3a72657c3 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -507,7 +507,7 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
 std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
 {
     std::string gcode;
-	assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size());
+    assert(m_layer_idx >= 0);
     if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
 		if (m_layer_idx < (int)m_tool_changes.size()) {
 			if (! (size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))