From 440e58573ed7ab82258adea44298d879459aa1d0 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 25 Jul 2018 16:11:31 +0200
Subject: [PATCH 01/22] Some documentation, C++11 conversion, code
 beautification, added some helper methods.

---
 xs/src/libslic3r/ExPolygonCollection.cpp      |  9 ++--
 xs/src/libslic3r/ExtrusionEntity.hpp          | 40 ++++++++------
 .../libslic3r/ExtrusionEntityCollection.hpp   | 16 ++++--
 xs/src/libslic3r/Flow.cpp                     |  9 ++--
 xs/src/libslic3r/Layer.hpp                    | 52 ++++++++-----------
 xs/src/libslic3r/LayerRegion.cpp              | 12 ++---
 xs/src/libslic3r/MultiPoint.hpp               |  4 +-
 xs/src/libslic3r/Polygon.hpp                  |  6 +++
 xs/src/libslic3r/Polyline.cpp                 | 20 +++----
 xs/src/libslic3r/Print.hpp                    |  3 ++
 xs/src/libslic3r/PrintConfig.hpp              |  8 +++
 xs/src/libslic3r/PrintRegion.cpp              |  5 ++
 xs/src/libslic3r/SurfaceCollection.hpp        |  5 ++
 xs/xsp/Layer.xsp                              |  2 -
 14 files changed, 109 insertions(+), 82 deletions(-)

diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp
index e52498ecb..6933544b6 100644
--- a/xs/src/libslic3r/ExPolygonCollection.cpp
+++ b/xs/src/libslic3r/ExPolygonCollection.cpp
@@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point &center)
 }
 
 template <class T>
-bool
-ExPolygonCollection::contains(const T &item) const
+bool ExPolygonCollection::contains(const T &item) const
 {
-    for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
-        if (it->contains(item)) return true;
-    }
+    for (const ExPolygon &poly : this->expolygons)
+        if (poly.contains(item))
+            return true;
     return false;
 }
 template bool ExPolygonCollection::contains<Point>(const Point &item) const;
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 15363e8ed..504d264fe 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -91,6 +91,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     virtual double min_mm3_per_mm() const = 0;
     virtual Polyline as_polyline() const = 0;
+    virtual void   collect_polylines(Polylines &dst) const = 0;
+    virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
     virtual double length() const = 0;
     virtual double total_volume() const = 0;
 };
@@ -123,8 +125,11 @@ public:
 
     ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
     void reverse() { this->polyline.reverse(); }
-    Point first_point() const { return this->polyline.points.front(); }
-    Point last_point() const { return this->polyline.points.back(); }
+    Point first_point() const override { return this->polyline.points.front(); }
+    Point last_point() const override { return this->polyline.points.back(); }
+    size_t size() const { return this->polyline.size(); }
+    bool empty() const { return this->polyline.empty(); }
+    bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
     // Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
     // Currently not used.
     void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
@@ -133,8 +138,8 @@ public:
     void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
     void clip_end(double distance);
     void simplify(double tolerance);
-    virtual double length() const;
-    virtual ExtrusionRole role() const { return m_role; }
+    double length() const override;
+    ExtrusionRole role() const override { return m_role; }
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -149,7 +154,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const { return this->mm3_per_mm; }
     Polyline as_polyline() const { return this->polyline; }
-    virtual double total_volume() const { return mm3_per_mm * unscale(length()); }
+    void   collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
+    double total_volume() const override { return mm3_per_mm * unscale(length()); }
 
 private:
     void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -178,10 +184,10 @@ public:
     bool can_reverse() const { return true; }
     ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
     void reverse();
-    Point first_point() const { return this->paths.front().polyline.points.front(); }
-    Point last_point() const { return this->paths.back().polyline.points.back(); }
-    virtual double length() const;
-    virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
+    Point first_point() const override { return this->paths.front().polyline.points.front(); }
+    Point last_point() const override { return this->paths.back().polyline.points.back(); }
+    double length() const override;
+    ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -196,7 +202,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const;
     Polyline as_polyline() const;
-    virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+    void   collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+    double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
 };
 
 // Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -218,18 +225,18 @@ public:
     bool make_clockwise();
     bool make_counter_clockwise();
     void reverse();
-    Point first_point() const { return this->paths.front().polyline.points.front(); }
-    Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
+    Point first_point() const override { return this->paths.front().polyline.points.front(); }
+    Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
     Polygon polygon() const;
-    virtual double length() const;
+    double length() const override;
     bool split_at_vertex(const Point &point);
     void split_at(const Point &point, bool prefer_non_overhang);
     void clip_end(double distance, ExtrusionPaths* paths) const;
     // Test, whether the point is extruded by a bridging flow.
     // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
     bool has_overhang_point(const Point &point) const;
-    virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
-    ExtrusionLoopRole     loop_role() const { return m_loop_role; }
+    ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
+    ExtrusionLoopRole loop_role() const { return m_loop_role; }
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -244,7 +251,8 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const;
     Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
-    virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+    void   collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+    double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
 
 private:
     ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 382455fe3..81582a94d 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -24,7 +24,7 @@ public:
     explicit operator ExtrusionPaths() const;
     
     bool is_collection() const { return true; };
-    virtual ExtrusionRole role() const {
+    ExtrusionRole role() const override {
         ExtrusionRole out = erNone;
         for (const ExtrusionEntity *ee : entities) {
             ExtrusionRole er = ee->role();
@@ -66,11 +66,11 @@ public:
     Point last_point() const { return this->entities.back()->last_point(); }
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
-    virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
+    void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
     // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
-    virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
+    void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
     Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
         { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
@@ -79,14 +79,20 @@ public:
     void flatten(ExtrusionEntityCollection* retval) const;
     ExtrusionEntityCollection flatten() const;
     double min_mm3_per_mm() const;
-    virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
+    double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
 
     // Following methods shall never be called on an ExtrusionEntityCollection.
     Polyline as_polyline() const {
         CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
         return Polyline();
     };
-    virtual double length() const {
+
+    void collect_polylines(Polylines &dst) const override {
+        for (ExtrusionEntity* extrusion_entity : this->entities)
+            extrusion_entity->collect_polylines(dst);
+    }
+
+    double length() const override {
         CONFESS("Calling length() on a ExtrusionEntityCollection");
         return 0.;        
     }
diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp
index b60e26dcc..e92674a17 100644
--- a/xs/src/libslic3r/Flow.cpp
+++ b/xs/src/libslic3r/Flow.cpp
@@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
         // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
         float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
         (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
-        false);
+        // bridge_flow_ratio
+        0.f);
 }
 
 Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
@@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
         (width.value > 0) ? width : object->config.extrusion_width,
         float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
         (layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
-        false);
+        // bridge_flow_ratio
+        0.f);
 }
 
 Flow support_material_interface_flow(const PrintObject *object, float layer_height)
@@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
         // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
         float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
         (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
-        false);
+        // bridge_flow_ratio
+        0.f);
 }
 
 }
diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp
index f3b460443..620583128 100644
--- a/xs/src/libslic3r/Layer.hpp
+++ b/xs/src/libslic3r/Layer.hpp
@@ -21,45 +21,37 @@ class LayerRegion
     friend class Layer;
 
 public:
-    Layer* layer() { return this->_layer; }
-    const Layer* layer() const { return this->_layer; }
-    PrintRegion* region() { return this->_region; }
-    const PrintRegion* region() const { return this->_region; }
+    Layer*              layer()        { return this->_layer; }
+    const Layer*        layer()  const { return this->_layer; }
+    PrintRegion*        region()       { return this->_region; }
+    const PrintRegion*  region() const { return this->_region; }
 
-    // collection of surfaces generated by slicing the original geometry
-    // divided by type top/bottom/internal
-    SurfaceCollection slices;
-
-    // collection of extrusion paths/loops filling gaps
-    // These fills are generated by the perimeter generator.
-    // They are not printed on their own, but they are copied to this->fills during infill generation.
-    ExtrusionEntityCollection thin_fills;
+    // Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal.
+    SurfaceCollection           slices;
 
     // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
     // and for re-starting of infills.
-    ExPolygons          fill_expolygons;
+    ExPolygons                  fill_expolygons;
     // collection of surfaces for infill generation
-    SurfaceCollection   fill_surfaces;
+    SurfaceCollection           fill_surfaces;
+    // Collection of extrusion paths/loops filling gaps.
+    // These fills are generated by the perimeter generator.
+    // They are not printed on their own, but they are copied to this->fills during infill generation.
+    ExtrusionEntityCollection   thin_fills;
 
-    // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
-    // While not necessary, the memory consumption is meager and it speeds up calculation.
-    // The perimeter_surfaces keep the IDs of the slices (top/bottom/)
-    SurfaceCollection perimeter_surfaces;
-
-    // collection of expolygons representing the bridged areas (thus not
-    // needing support material)
-    Polygons bridged;
+    // Collection of expolygons representing the bridged areas (thus not needing support material).
+    //FIXME Not used as of now.
+    Polygons                    bridged;
 
     // collection of polylines representing the unsupported bridge edges
-    PolylineCollection unsupported_bridge_edges;
+    PolylineCollection          unsupported_bridge_edges;
 
-    // ordered collection of extrusion paths/loops to build all perimeters
-    // (this collection contains only ExtrusionEntityCollection objects)
-    ExtrusionEntityCollection perimeters;
-
-    // ordered collection of extrusion paths to fill surfaces
-    // (this collection contains only ExtrusionEntityCollection objects)
-    ExtrusionEntityCollection fills;
+    // Ordered collection of extrusion paths/loops to build all perimeters.
+    // This collection contains only ExtrusionEntityCollection objects.
+    ExtrusionEntityCollection   perimeters;
+    // Ordered collection of extrusion paths to fill surfaces.
+    // This collection contains only ExtrusionEntityCollection objects.
+    ExtrusionEntityCollection   fills;
     
     Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
     void slices_to_fill_surfaces_clipped();
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index 68e17407e..12526f2ec 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -15,8 +15,7 @@
 
 namespace Slic3r {
 
-Flow
-LayerRegion::flow(FlowRole role, bool bridge, double width) const
+Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
 {
     return this->_region->flow(
         role,
@@ -51,8 +50,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
     }
 }
 
-void
-LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
+void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
 {
     this->perimeters.clear();
     this->thin_fills.clear();
@@ -340,8 +338,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 }
 
-void
-LayerRegion::prepare_fill_surfaces()
+void LayerRegion::prepare_fill_surfaces()
 {
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
     export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial");
@@ -382,8 +379,7 @@ LayerRegion::prepare_fill_surfaces()
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 }
 
-double
-LayerRegion::infill_area_threshold() const
+double LayerRegion::infill_area_threshold() const
 {
     double ss = this->flow(frSolidInfill).scaled_spacing();
     return ss*ss;
diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp
index 0970e9a67..f4f82a353 100644
--- a/xs/src/libslic3r/MultiPoint.hpp
+++ b/xs/src/libslic3r/MultiPoint.hpp
@@ -34,8 +34,10 @@ public:
     Point first_point() const;
     virtual Point last_point() const = 0;
     virtual Lines lines() const = 0;
+    size_t size() const { return points.size(); }
+    bool   empty() const { return points.empty(); }
     double length() const;
-    bool is_valid() const { return this->points.size() >= 2; }
+    bool   is_valid() const { return this->points.size() >= 2; }
 
     int  find_point(const Point &point) const;
     bool has_boundary_point(const Point &point) const;
diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 1a02d78b7..2d624e71a 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
         p.rotate(cos_angle, sin_angle);
 }
 
+inline void polygons_reverse(Polygons &polys)
+{
+    for (Polygon &p : polys)
+        p.reverse();
+}
+
 inline Points to_points(const Polygon &poly)
 {
     return poly.points;
diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp
index 3432506c6..05bd8c7fb 100644
--- a/xs/src/libslic3r/Polyline.cpp
+++ b/xs/src/libslic3r/Polyline.cpp
@@ -193,23 +193,19 @@ Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
     }
 }
 
-bool
-Polyline::is_straight() const
+bool Polyline::is_straight() const
 {
-    /*  Check that each segment's direction is equal to the line connecting
-        first point and last point. (Checking each line against the previous
-        one would cause the error to accumulate.) */
+    // Check that each segment's direction is equal to the line connecting
+    // first point and last point. (Checking each line against the previous
+    // one would cause the error to accumulate.)
     double dir = Line(this->first_point(), this->last_point()).direction();
-    
-    Lines lines = this->lines();
-    for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
-        if (!line->parallel_to(dir)) return false;
-    }
+    for (const auto &line: this->lines())
+        if (! line.parallel_to(dir))
+            return false;
     return true;
 }
 
-std::string
-Polyline::wkt() const
+std::string Polyline::wkt() const
 {
     std::ostringstream wkt;
     wkt << "LINESTRING((";
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index bcd61ea02..67665fbcb 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -80,7 +80,10 @@ public:
 
     Print* print() { return this->_print; }
     Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
+    // Average diameter of nozzles participating on extruding this region.
     coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
+    // Average diameter of nozzles participating on extruding this region.
+    coordf_t bridging_height_avg(const PrintConfig &print_config) const;
 
 private:
     Print* _print;
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index c530868a1..ba9c3fc8f 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -310,6 +310,7 @@ public:
     ConfigOptionFloatOrPercent      extrusion_width;
     ConfigOptionFloatOrPercent      first_layer_height;
     ConfigOptionBool                infill_only_where_needed;
+    // Force the generation of solid shells between adjacent materials/volumes.
     ConfigOptionBool                interface_shells;
     ConfigOptionFloat               layer_height;
     ConfigOptionInt                 raft_layers;
@@ -317,6 +318,7 @@ public:
 //    ConfigOptionFloat               seam_preferred_direction;
 //    ConfigOptionFloat               seam_preferred_direction_jitter;
     ConfigOptionBool                support_material;
+    // Direction of the support pattern (in XY plane).
     ConfigOptionFloat               support_material_angle;
     ConfigOptionBool                support_material_buildplate_only;
     ConfigOptionFloat               support_material_contact_distance;
@@ -326,12 +328,15 @@ public:
     ConfigOptionBool                support_material_interface_contact_loops;
     ConfigOptionInt                 support_material_interface_extruder;
     ConfigOptionInt                 support_material_interface_layers;
+    // Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
     ConfigOptionFloat               support_material_interface_spacing;
     ConfigOptionFloatOrPercent      support_material_interface_speed;
     ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
+    // Spacing between support material lines (the hatching distance).
     ConfigOptionFloat               support_material_spacing;
     ConfigOptionFloat               support_material_speed;
     ConfigOptionBool                support_material_synchronize_layers;
+    // Overhang angle threshold.
     ConfigOptionInt                 support_material_threshold;
     ConfigOptionBool                support_material_with_sheath;
     ConfigOptionFloatOrPercent      support_material_xy_spacing;
@@ -401,10 +406,12 @@ public:
     ConfigOptionInt                 infill_every_layers;
     ConfigOptionFloatOrPercent      infill_overlap;
     ConfigOptionFloat               infill_speed;
+    // Detect bridging perimeters
     ConfigOptionBool                overhangs;
     ConfigOptionInt                 perimeter_extruder;
     ConfigOptionFloatOrPercent      perimeter_extrusion_width;
     ConfigOptionFloat               perimeter_speed;
+    // Total number of perimeters.
     ConfigOptionInt                 perimeters;
     ConfigOptionFloatOrPercent      small_perimeter_speed;
     ConfigOptionFloat               solid_infill_below_area;
@@ -412,6 +419,7 @@ public:
     ConfigOptionFloatOrPercent      solid_infill_extrusion_width;
     ConfigOptionInt                 solid_infill_every_layers;
     ConfigOptionFloatOrPercent      solid_infill_speed;
+    // Detect thin walls.
     ConfigOptionBool                thin_walls;
     ConfigOptionFloatOrPercent      top_infill_extrusion_width;
     ConfigOptionInt                 top_solid_layers;
diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp
index 4874c71bc..71fcc876a 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -57,4 +57,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
             print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.;
 }
 
+coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
+{
+    return this->nozzle_dmr_avg(print_config) * this->config.bridge_flow_ratio.value;
+}
+
 }
diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp
index 29cfeb1db..9544748e9 100644
--- a/xs/src/libslic3r/SurfaceCollection.hpp
+++ b/xs/src/libslic3r/SurfaceCollection.hpp
@@ -37,6 +37,11 @@ public:
 
     void clear() { surfaces.clear(); }
     bool empty() const { return surfaces.empty(); }
+    bool has(SurfaceType type) const { 
+        for (const Surface &surface : this->surfaces) 
+            if (surface.surface_type == type) return true;
+        return false;
+    }
 
     void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
     void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index 4f09fb521..efd6c9ae6 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -17,8 +17,6 @@
         %code%{ RETVAL = &THIS->thin_fills; %};
     Ref<SurfaceCollection> fill_surfaces()
         %code%{ RETVAL = &THIS->fill_surfaces; %};
-    Ref<SurfaceCollection> perimeter_surfaces()
-        %code%{ RETVAL = &THIS->perimeter_surfaces; %};
     Polygons bridged()
         %code%{ RETVAL = THIS->bridged; %};
     Ref<PolylineCollection> unsupported_bridge_edges()

From a7fbb70bcf0a2a8546b539b6ac0e2adc5ba2693d Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 25 Jul 2018 16:20:15 +0200
Subject: [PATCH 02/22] Fixed an incorrect offset between the obect bottom
 layer and the supports. (bridging_flow correction has not been considered, so
 the bridge was printed with a bridging flow, but an offset was calculated for
 a nozzle height). Fixed some missing interface layers
 (trim_support_layers_by_object() was too aggressive). Fixed
 trim_support_layers_by_object() to consider the bridging perimeters().

---
 xs/src/libslic3r/SupportMaterial.cpp | 241 +++++++++++++++++----------
 1 file changed, 155 insertions(+), 86 deletions(-)

diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 0cecf0014..538139c27 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -248,10 +248,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
 #ifdef SLIC3R_DEBUG
     static int iRun = 0;
     iRun ++;
-    for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
+    for (const MyLayer *layer : top_contacts)
         Slic3r::SVG::export_expolygons(
-            debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z), 
-            union_ex((*it)->polygons, false));
+            debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), 
+            union_ex(layer->polygons, false));
 #endif /* SLIC3R_DEBUG */
 
     BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts";
@@ -282,7 +282,15 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
     MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers(
         object, bottom_contacts, top_contacts, layer_storage);
 
-    this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+//    this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+    this->trim_support_layers_by_object(object, top_contacts, 0., 0., m_gap_xy);
+
+#ifdef SLIC3R_DEBUG
+    for (const MyLayer *layer : top_contacts)
+        Slic3r::SVG::export_expolygons(
+            debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), 
+            union_ex(layer->polygons, false));
+#endif
 
     BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
 
@@ -632,6 +640,69 @@ private:
     Points                  m_island_samples;
 };
 
+namespace SupportMaterialInternal {
+    static inline bool has_bridging_perimeters(const ExtrusionLoop &loop)
+    {
+        for (const ExtrusionPath &ep : loop.paths)
+            if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
+                return ep.size() >= (ep.is_closed() ? 3 : 2);
+            return false;
+    }
+    static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
+    {
+        for (const ExtrusionEntity *ee : perimeters.entities) {
+            if (ee->is_collection()) {
+                for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+                    assert(! ee2->is_collection());
+                    if (ee2->is_loop())
+                        if (has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee2)))
+                            return true;
+                }
+            } else if (ee->is_loop() && has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee)))
+                return true;
+        }
+        return false;
+    }
+
+    static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out)
+    {
+        assert(expansion_scaled >= 0.f);
+        for (const ExtrusionPath &ep : loop.paths)
+            if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
+                float exp = 0.5f * scale_(ep.width) + expansion_scaled;
+                if (ep.is_closed()) {
+                    if (ep.size() >= 3) {
+                        // This is a complete loop.
+                        // Add the outer contour first.
+                        Polygon poly;
+                        poly.points = ep.polyline.points;
+                        poly.points.pop_back();
+                        polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                        Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                        polygons_reverse(holes);
+                        polygons_append(out, holes);
+                    }
+                } else if (ep.size() >= 2) {
+                    // Offset the polyline.
+                    offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                }
+            }
+    }
+    static void collect_bridging_perimeter_areas(const ExtrusionEntityCollection &perimeters, const float expansion_scaled, Polygons &out)
+    {
+        for (const ExtrusionEntity *ee : perimeters.entities) {
+            if (ee->is_collection()) {
+                for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+                    assert(! ee2->is_collection());
+                    if (ee2->is_loop())
+                        collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee2), expansion_scaled, out);
+                }
+            } else if (ee->is_loop())
+                collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee), expansion_scaled, out);
+        }
+    }
+}
+
 // Generate top contact layers supporting overhangs.
 // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
 // If supports over bed surface only are requested, don't generate contact layers over an object.
@@ -764,70 +835,60 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
 
                         if (this->m_object_config->dont_support_bridges) {
                             // compute the area of bridging perimeters
-                            // Note: this is duplicate code from GCode.pm, we need to refactor
-                            if (true) {
-                                Polygons bridged_perimeters;
-                                {
-                                    Flow bridge_flow = layerm->flow(frPerimeter, true);
-                                    coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1);
-                                    Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS);
-                                    
-                                    // Collect perimeters of this layer.
-                                    // TODO: split_at_first_point() could split a bridge mid-way
-                                    Polylines overhang_perimeters;
-                                    for (ExtrusionEntity* extrusion_entity : layerm->perimeters.entities) {
-                                        const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(extrusion_entity);
-                                        assert(island != NULL);
-                                        for (size_t i = 0; i < island->entities.size(); ++ i) {
-                                            ExtrusionEntity *entity = island->entities[i];
-                                            ExtrusionLoop *loop = dynamic_cast<Slic3r::ExtrusionLoop*>(entity);
-                                            overhang_perimeters.push_back(loop ? 
-                                                loop->as_polyline() :
-                                                dynamic_cast<const Slic3r::ExtrusionPath*>(entity)->polyline);
-                                        }
+                            Polygons bridges;
+                            {
+                                // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
+                                Polygons lower_grown_slices = offset(lower_layer_polygons, 
+                                    //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
+                                    0.5f * float(scale_(m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
+                                    SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                                // Collect perimeters of this layer.
+                                //FIXME split_at_first_point() could split a bridge mid-way
+                            #if 0
+                                Polylines overhang_perimeters = layerm->perimeters.as_polylines();
+                                // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
+                                for (Polyline &polyline : overhang_perimeters)
+                                    polyline.points[0].x += 1;
+                                // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
+                                overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
+                            #else
+                                Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
+                            #endif
+                                
+                                // only consider straight overhangs
+                                // only consider overhangs having endpoints inside layer's slices
+                                // convert bridging polylines into polygons by inflating them with their thickness
+                                // since we're dealing with bridges, we can't assume width is larger than spacing,
+                                // so we take the largest value and also apply safety offset to be ensure no gaps
+                                // are left in between
+                                Flow bridge_flow = layerm->flow(frPerimeter, true);
+                                float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
+                                for (Polyline &polyline : overhang_perimeters)
+                                    if (polyline.is_straight()) {
+                                        // This is a bridge 
+                                        polyline.extend_start(fw);
+                                        polyline.extend_end(fw);
+                                        // Is the straight perimeter segment supported at both sides?
+                                        if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
+                                            // Offset a polyline into a thick line.
+                                            polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
                                     }
-                                    
-                                    // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
-                                    for (Polyline &polyline : overhang_perimeters)
-                                        polyline.points[0].x += 1;
-                                    // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
-                                    overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
-                                    
-                                    // only consider straight overhangs
-                                    // only consider overhangs having endpoints inside layer's slices
-                                    // convert bridging polylines into polygons by inflating them with their thickness
-                                    // since we're dealing with bridges, we can't assume width is larger than spacing,
-                                    // so we take the largest value and also apply safety offset to be ensure no gaps
-                                    // are left in between
-                                    float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
-                                    for (Polyline &polyline : overhang_perimeters)
-                                        if (polyline.is_straight()) {
-                                            // This is a bridge 
-                                            polyline.extend_start(fw);
-                                            polyline.extend_end(fw);
-                                            // Is the straight perimeter segment supported at both sides?
-                                            if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point()))
-                                                // Offset a polyline into a thick line.
-                                                polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f));
-                                        }
-                                    bridged_perimeters = union_(bridged_perimeters);
-                                }
-                                // remove the entire bridges and only support the unsupported edges
-                                Polygons bridges;
-                                for (const Surface &surface : layerm->fill_surfaces.surfaces)
-                                    if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
-                                        polygons_append(bridges, surface.expolygon);
-                                diff_polygons = diff(diff_polygons, bridges, true);
-                                polygons_append(bridges, bridged_perimeters);
-                                polygons_append(diff_polygons, 
-                                    intersection(
-                                        // Offset unsupported edges into polygons.
-                                        offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
-                                        bridges));
-                            } else {
-                                // just remove bridged areas
-                                diff_polygons = diff(diff_polygons, layerm->bridged, true);
+                                bridges = union_(bridges);
                             }
+                            // remove the entire bridges and only support the unsupported edges
+                            //FIXME the brided regions are already collected as layerm->bridged. Use it?
+                            for (const Surface &surface : layerm->fill_surfaces.surfaces)
+                                if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
+                                    polygons_append(bridges, surface.expolygon);
+                            //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
+                            diff_polygons = diff(diff_polygons, bridges, true);
+                            // Add the bridge anchors into the region.
+                            //FIXME add supports at regular intervals to support long bridges!
+                            polygons_append(diff_polygons,
+                                intersection(
+                                    // Offset unsupported edges into polygons.
+                                    offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
+                                    bridges));
                         } // if (m_objconfig->dont_support_bridges)
 
                         if (diff_polygons.empty())
@@ -879,9 +940,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                     } // for each layer.region
                 } // end of Generate overhang/contact_polygons for non-raft layers.
                 
-                // now apply the contact areas to the layer were they need to be made
+                // Now apply the contact areas to the layer where they need to be made.
                 if (! contact_polygons.empty()) {
-                    // get the average nozzle diameter used on this layer
                     MyLayer     &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
                     new_layer.idx_object_layer_above = layer_id;
                     if (m_slicing_params.soluble_interface) {
@@ -903,11 +963,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow?
                         // In the future we may switch to a normal extrusion flow for the supported bridges.
                         // Get the average nozzle diameter used on this layer.
-                        coordf_t nozzle_dmr = 0.;
+                        coordf_t bridging_height = 0.;
                         for (const LayerRegion *region : layer.regions)
-                            nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config);
-                        nozzle_dmr /= coordf_t(layer.regions.size());
-                        new_layer.print_z  = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance;
+                            bridging_height += region->region()->bridging_height_avg(*m_print_config);
+                        bridging_height /= coordf_t(layer.regions.size());
+                        new_layer.print_z  = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
                         new_layer.bottom_z = new_layer.print_z;
                         new_layer.height   = 0.;
                         if (layer_id == 0) {
@@ -996,7 +1056,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
             const Layer &layer = *object.get_layer(layer_id);
             // Collect projections of all contact areas above or at the same level as this top surface.
-            for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
+            for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) {
+                auto *l = top_contacts[contact_idx];
                 Polygons polygons_new;
                 // Contact surfaces are expanded away from the object, trimmed by the object.
                 // Use a slight positive offset to overlap the touching regions.
@@ -1046,10 +1107,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                             // Grow top surfaces so that interface and support generation are generated
                             // with some spacing from object - it looks we don't need the actual
                             // top shapes so this can be done here
+                            //FIXME calculate layer height based on the actual thickness of the layer:
+                            // If the layer is extruded with no bridging flow, support just the normal extrusions.
                             layer_new.height  = m_slicing_params.soluble_interface ? 
                                 // Align the interface layer with the object's layer height.
                                 object.layers[layer_id + 1]->height :
                                 // Place a bridge flow interface layer over the top surface.
+                                //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
+                                // According to Jindrich the bottom surfaces work well.
                                 m_support_material_interface_flow.nozzle_diameter;
                             layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
                                 layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
@@ -1185,7 +1250,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             task_group.wait();
         }
         std::reverse(bottom_contacts.begin(), bottom_contacts.end());
-        trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+        trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
     } // ! top_contacts.empty()
 
     return bottom_contacts;
@@ -1608,7 +1673,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
     ++ iRun;
 #endif /* SLIC3R_DEBUG */
 
-    trim_support_layers_by_object(object, intermediate_layers,  m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min,  m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_gap_xy);
+    trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
 }
 
 void PrintObjectSupportMaterial::trim_support_layers_by_object(
@@ -1660,12 +1725,16 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                     for (; i < object.layers.size(); ++ i) {
                         const Layer &object_layer = *object.layers[i];
                         bool some_region_overlaps = false;
-                        for (LayerRegion* region : object_layer.regions) {
-                            coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*this->m_print_config);
-                            if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON)
+                        for (LayerRegion *region : object_layer.regions) {
+                            coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
+                            if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
                                 break;
                             some_region_overlaps = true;
-                            polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge)));
+                            polygons_append(polygons_trimming, 
+                                offset(to_expolygons(region->slices.filter_by_type(stBottomBridge)), 
+                                       gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                            if (region->region()->config.overhangs.value)
+                                SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
                         }
                         if (! some_region_overlaps)
                             break;
@@ -1675,9 +1744,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                 // perimeter's width. $support contains the full shape of support
                 // material, thus including the width of its foremost extrusion.
                 // We leave a gap equal to a full extrusion width.
-                support_layer.polygons = diff(
-                    support_layer.polygons,
-                    offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
             }
         });
     BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
@@ -1861,8 +1928,8 @@ static inline void fill_expolygons_generate_paths(
     fill_params.density = density;
     fill_params.complete = true;
     fill_params.dont_adjust = true;
-    for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
-        Surface surface(stInternal, *it_expolygon);
+    for (const ExPolygon &expoly : expolygons) {
+        Surface surface(stInternal, expoly);
         extrusion_entities_append_paths(
             dst,
             filler->fill_surface(&surface, fill_params),
@@ -1883,8 +1950,8 @@ static inline void fill_expolygons_generate_paths(
     fill_params.density = density;
     fill_params.complete = true;
     fill_params.dont_adjust = true;
-    for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
-        Surface surface(stInternal, std::move(*it_expolygon));
+    for (ExPolygon &expoly : expolygons) {
+        Surface surface(stInternal, std::move(expoly));
         extrusion_entities_append_paths(
             dst,
             filler->fill_surface(&surface, fill_params),
@@ -2711,6 +2778,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
                     continue;
                 //FIXME When paralellizing, each thread shall have its own copy of the fillers.
                 bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
+                //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
+                // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
                 Flow interface_flow(
                     float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)),
                     float(layer_ex.layer->height),

From 9d0093e474ccecd2b4bb6a367d679bc1341700c7 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 26 Jul 2018 11:33:57 +0200
Subject: [PATCH 03/22] Support generator now produce two contact layers per
 object layer: One contact layer to support the bridging flow extrusions, and
 the other contact layer to support the non-bridging flow extrusions.

---
 xs/src/libslic3r/SupportMaterial.cpp | 295 +++++++++++++++++----------
 xs/src/libslic3r/SupportMaterial.hpp |  15 ++
 2 files changed, 207 insertions(+), 103 deletions(-)

diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 538139c27..79bd66dfc 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -663,6 +663,29 @@ namespace SupportMaterialInternal {
         }
         return false;
     }
+    static bool has_bridging_fills(const ExtrusionEntityCollection &fills)
+    {
+        for (const ExtrusionEntity *ee : fills.entities) {
+            assert(ee->is_collection());
+            for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+                assert(! ee2->is_collection());
+                assert(! ee2->is_loop());
+                if (ee2->role() == erBridgeInfill)
+                    return true;
+            }
+        }
+        return false;
+    }
+    static bool has_bridging_extrusions(const Layer &layer) 
+    {
+        for (const LayerRegion *region : layer.regions) {
+            if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
+                return true;
+            if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
+                return true;
+        }
+        return false;
+    }
 
     static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out)
     {
@@ -677,6 +700,8 @@ namespace SupportMaterialInternal {
                         Polygon poly;
                         poly.points = ep.polyline.points;
                         poly.points.pop_back();
+                        if (poly.area() < 0)
+                            poly.reverse();
                         polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                         Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
                         polygons_reverse(holes);
@@ -684,7 +709,7 @@ namespace SupportMaterialInternal {
                     }
                 } else if (ep.size() >= 2) {
                     // Offset the polyline.
-                    offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                    polygons_append(out, offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                 }
             }
     }
@@ -701,6 +726,71 @@ namespace SupportMaterialInternal {
                 collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee), expansion_scaled, out);
         }
     }
+
+    static void remove_bridges_from_contacts(
+        const PrintConfig   &print_config, 
+        const Layer         &lower_layer,
+        const Polygons      &lower_layer_polygons,
+        LayerRegion         *layerm,
+        float                fw, 
+        Polygons            &contact_polygons)
+    {
+        // compute the area of bridging perimeters
+        Polygons bridges;
+        {
+            // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
+            Polygons lower_grown_slices = offset(lower_layer_polygons, 
+                //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
+                0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
+                SUPPORT_SURFACES_OFFSET_PARAMETERS);
+            // Collect perimeters of this layer.
+            //FIXME split_at_first_point() could split a bridge mid-way
+        #if 0
+            Polylines overhang_perimeters = layerm->perimeters.as_polylines();
+            // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
+            for (Polyline &polyline : overhang_perimeters)
+                polyline.points[0].x += 1;
+            // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
+            overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
+        #else
+            Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
+        #endif
+            
+            // only consider straight overhangs
+            // only consider overhangs having endpoints inside layer's slices
+            // convert bridging polylines into polygons by inflating them with their thickness
+            // since we're dealing with bridges, we can't assume width is larger than spacing,
+            // so we take the largest value and also apply safety offset to be ensure no gaps
+            // are left in between
+            Flow bridge_flow = layerm->flow(frPerimeter, true);
+            float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
+            for (Polyline &polyline : overhang_perimeters)
+                if (polyline.is_straight()) {
+                    // This is a bridge 
+                    polyline.extend_start(fw);
+                    polyline.extend_end(fw);
+                    // Is the straight perimeter segment supported at both sides?
+                    if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
+                        // Offset a polyline into a thick line.
+                        polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
+                }
+            bridges = union_(bridges);
+        }
+        // remove the entire bridges and only support the unsupported edges
+        //FIXME the brided regions are already collected as layerm->bridged. Use it?
+        for (const Surface &surface : layerm->fill_surfaces.surfaces)
+            if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
+                polygons_append(bridges, surface.expolygon);
+        //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
+        contact_polygons = diff(contact_polygons, bridges, true);
+        // Add the bridge anchors into the region.
+        //FIXME add supports at regular intervals to support long bridges!
+        polygons_append(contact_polygons,
+            intersection(
+                // Offset unsupported edges into polygons.
+                offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
+                bridges));
+    }
 }
 
 // Generate top contact layers supporting overhangs.
@@ -751,7 +841,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
     // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
     size_t num_layers = this->has_support() ? object.layer_count() : 1;
-    contact_out.assign(num_layers, nullptr);
+    contact_out.assign(num_layers * 2, nullptr);
     tbb::spin_mutex layer_storage_mutex;
     tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
         [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
@@ -833,63 +923,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         }
                         #endif /* SLIC3R_DEBUG */
 
-                        if (this->m_object_config->dont_support_bridges) {
-                            // compute the area of bridging perimeters
-                            Polygons bridges;
-                            {
-                                // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
-                                Polygons lower_grown_slices = offset(lower_layer_polygons, 
-                                    //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
-                                    0.5f * float(scale_(m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
-                                    SUPPORT_SURFACES_OFFSET_PARAMETERS);
-                                // Collect perimeters of this layer.
-                                //FIXME split_at_first_point() could split a bridge mid-way
-                            #if 0
-                                Polylines overhang_perimeters = layerm->perimeters.as_polylines();
-                                // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
-                                for (Polyline &polyline : overhang_perimeters)
-                                    polyline.points[0].x += 1;
-                                // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
-                                overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
-                            #else
-                                Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
-                            #endif
-                                
-                                // only consider straight overhangs
-                                // only consider overhangs having endpoints inside layer's slices
-                                // convert bridging polylines into polygons by inflating them with their thickness
-                                // since we're dealing with bridges, we can't assume width is larger than spacing,
-                                // so we take the largest value and also apply safety offset to be ensure no gaps
-                                // are left in between
-                                Flow bridge_flow = layerm->flow(frPerimeter, true);
-                                float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
-                                for (Polyline &polyline : overhang_perimeters)
-                                    if (polyline.is_straight()) {
-                                        // This is a bridge 
-                                        polyline.extend_start(fw);
-                                        polyline.extend_end(fw);
-                                        // Is the straight perimeter segment supported at both sides?
-                                        if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
-                                            // Offset a polyline into a thick line.
-                                            polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
-                                    }
-                                bridges = union_(bridges);
-                            }
-                            // remove the entire bridges and only support the unsupported edges
-                            //FIXME the brided regions are already collected as layerm->bridged. Use it?
-                            for (const Surface &surface : layerm->fill_surfaces.surfaces)
-                                if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
-                                    polygons_append(bridges, surface.expolygon);
-                            //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
-                            diff_polygons = diff(diff_polygons, bridges, true);
-                            // Add the bridge anchors into the region.
-                            //FIXME add supports at regular intervals to support long bridges!
-                            polygons_append(diff_polygons,
-                                intersection(
-                                    // Offset unsupported edges into polygons.
-                                    offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
-                                    bridges));
-                        } // if (m_objconfig->dont_support_bridges)
+                        if (this->m_object_config->dont_support_bridges)
+                            SupportMaterialInternal::remove_bridges_from_contacts(
+                                *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons);
 
                         if (diff_polygons.empty())
                             continue;
@@ -944,53 +980,68 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                 if (! contact_polygons.empty()) {
                     MyLayer     &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
                     new_layer.idx_object_layer_above = layer_id;
-                    if (m_slicing_params.soluble_interface) {
+                    MyLayer     *bridging_layer = nullptr;
+                    if (layer_id == 0) {
+                        // This is a raft contact layer sitting directly on the print bed.
+                        assert(this->has_raft());
+                        new_layer.print_z  = m_slicing_params.raft_contact_top_z;
+                        new_layer.bottom_z = m_slicing_params.raft_interface_top_z; 
+                        new_layer.height   = m_slicing_params.contact_raft_layer_height;
+                    } else if (m_slicing_params.soluble_interface) {
                         // Align the contact surface height with a layer immediately below the supported layer.
-                        new_layer.print_z = layer.print_z - layer.height;
-                        if (layer_id == 0) {
-                            // This is a raft contact layer sitting directly on the print bed.
-                            new_layer.height   = m_slicing_params.contact_raft_layer_height;
-                            new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
-                        } else {
-                            // Interface layer will be synchronized with the object.
-                            assert(layer_id > 0);
-                            new_layer.height = object.layers[layer_id - 1]->height;
-                            new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
-                        }
+                        // Interface layer will be synchronized with the object.
+                        new_layer.print_z  = layer.print_z - layer.height;
+                        new_layer.height   = object.layers[layer_id - 1]->height;
+                        new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
                     } else {
-                        // Contact layer will be printed with a normal flow, but
-                        // it will support layers printed with a bridging flow.
-                        //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow?
-                        // In the future we may switch to a normal extrusion flow for the supported bridges.
-                        // Get the average nozzle diameter used on this layer.
-                        coordf_t bridging_height = 0.;
-                        for (const LayerRegion *region : layer.regions)
-                            bridging_height += region->region()->bridging_height_avg(*m_print_config);
-                        bridging_height /= coordf_t(layer.regions.size());
-                        new_layer.print_z  = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
+                        new_layer.print_z  = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
                         new_layer.bottom_z = new_layer.print_z;
                         new_layer.height   = 0.;
-                        if (layer_id == 0) {
-                            // This is a raft contact layer sitting directly on the print bed.
-                            assert(this->has_raft());
-                            new_layer.bottom_z = m_slicing_params.raft_interface_top_z; 
-                            new_layer.height   = m_slicing_params.contact_raft_layer_height;
+                        // Ignore this contact area if it's too low.
+                        // Don't want to print a layer below the first layer height as it may not stick well.
+                        //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
+                        // and it may actually make sense to do it with a thinner layer than the first layer height.
+                        if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
+                            // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
+                            continue;
+                        } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+                            // Align the layer with the 1st layer height.
+                            new_layer.print_z  = m_slicing_params.first_print_layer_height;
+                            new_layer.bottom_z = 0;
+                            new_layer.height   = m_slicing_params.first_print_layer_height;
                         } else {
-                            // Ignore this contact area if it's too low.
-                            // Don't want to print a layer below the first layer height as it may not stick well.
-                            //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
-                            // and it may actually make sense to do it with a thinner layer than the first layer height.
-                            if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
-                                // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
-                                continue;
-                            } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
-                                // Align the layer with the 1st layer height.
-                                new_layer.print_z  = m_slicing_params.first_print_layer_height;
-                                new_layer.bottom_z = 0;
-                                new_layer.height   = m_slicing_params.first_print_layer_height;
-                            } else {
-                                // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and 
-                                // its height will be set adaptively later on.
+                            // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and 
+                            // its height will be set adaptively later on.
+                        }
+
+                        // Contact layer will be printed with a normal flow, but
+                        // it will support layers printed with a bridging flow.
+                        if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
+                            coordf_t bridging_height = 0.;
+                            for (const LayerRegion *region : layer.regions)
+                                bridging_height += region->region()->bridging_height_avg(*m_print_config);
+                            bridging_height /= coordf_t(layer.regions.size());
+                            coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
+                            if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
+                                // Not below the first layer height means this layer is printable.
+                                if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+                                    // Align the layer with the 1st layer height.
+                                    bridging_print_z = m_slicing_params.first_print_layer_height;
+                                }
+                                if (bridging_print_z != new_layer.print_z) {
+                                    // Allocate the new layer.
+                                    bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
+                                    bridging_layer->idx_object_layer_above = layer_id;
+                                    bridging_layer->print_z = bridging_print_z;
+                                    if (bridging_print_z == m_slicing_params.first_print_layer_height) {
+                                        bridging_layer->bottom_z = 0;
+                                        bridging_layer->height   = m_slicing_params.first_print_layer_height;                                        
+                                    } else {
+                                        // Don't know the height yet.
+                                        bridging_layer->bottom_z = bridging_print_z;
+                                        bridging_layer->height   = 0;
+                                    }
+                                }
                             }
                         }
                     }
@@ -1015,12 +1066,50 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                     // The overhang polygons are used in the path generator for planning of the contact loops.
                     // if (this->has_contact_loops())
                     new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
-                    contact_out[layer_id] = &new_layer;
+                    contact_out[layer_id * 2] = &new_layer;
+                    if (bridging_layer != nullptr) {
+                        bridging_layer->polygons          = new_layer.polygons;
+                        bridging_layer->contact_polygons  = new Polygons(*new_layer.contact_polygons);
+                        bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons);
+                        contact_out[layer_id * 2 + 1] = bridging_layer;
+                    }
                 }
             }
         });
+
     // Compress contact_out, remove the nullptr items.
     remove_nulls(contact_out);
+    // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z.
+    std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; });
+
+    // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
+    // the top contact layer is merged into the bottom contact layer.
+    {
+        int k = 0;
+        for (int i = 0; i < int(contact_out.size()); ++ k) {
+            // Find the span of layers closer than m_support_layer_height_min.
+            int j = i + 1;
+            coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min - EPSILON;
+            for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
+            if (i + 1 < j) {
+                // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
+                MyLayer &dst = *contact_out[i];
+                for (int u = i + 1; u < j; ++ u) {
+                    MyLayer &src = *contact_out[u];
+                    // The union_() does not support move semantic yet, but maybe one day it will.
+                    dst.polygons           = union_(dst.polygons, std::move(src.polygons));
+                    *dst.contact_polygons  = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+                    *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+                    // Source polygon is no more needed, it will not be refrenced. Release its data.
+                    src.reset();
+                }
+            }
+            if (k < i)
+                contact_out[k] = contact_out[i];
+            i = j;
+        }
+    }
+
     BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
 
     return contact_out;
@@ -1731,7 +1820,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                                 break;
                             some_region_overlaps = true;
                             polygons_append(polygons_trimming, 
-                                offset(to_expolygons(region->slices.filter_by_type(stBottomBridge)), 
+                                offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)), 
                                        gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                             if (region->region()->config.overhangs.value)
                                 SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index 968763446..da11cd82c 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -71,6 +71,21 @@ public:
 			overhang_polygons = nullptr;
 		}
 
+		void reset() {
+			layer_type  			= sltUnknown;
+			print_z 				= 0.;
+			bottom_z 				= 0.;
+			height 					= 0.;
+			idx_object_layer_above  = size_t(-1);
+			idx_object_layer_below  = size_t(-1);
+			bridging 				= false;
+			polygons.clear();
+			delete contact_polygons;
+			contact_polygons 		= nullptr;
+			delete overhang_polygons;
+			overhang_polygons 		= nullptr;
+		}
+
 		bool operator==(const MyLayer &layer2) const {
 			return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
 		}

From 53a7d05dcb3d35342c5f4fbefeda5ec305fba571 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 26 Jul 2018 19:24:39 +0200
Subject: [PATCH 04/22] Starting logging SublimeCodeIntel v2.2.0 rev
 GIT-unknown (1481616156) on Thu Jul 26 19:24:37 2018
 =================================================================================================

---
 xs/src/libslic3r/SupportMaterial.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 79bd66dfc..1bf9675df 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -1108,6 +1108,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                 contact_out[k] = contact_out[i];
             i = j;
         }
+        if (k < contact_out.size())
+            contact_out.erase(contact_out.begin() + k, contact_out.end());
     }
 
     BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";

From 00e9f07a03042de25438ec5922618dbb87f86be6 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 8 Aug 2018 16:24:10 +0200
Subject: [PATCH 05/22] Improved robustness of slicing when the slicing plane
 crosses a horizontal plane of an object exactly. Should improve Github issues
 #831, #895, #1102

---
 xs/src/libslic3r/Fill/FillHoneycomb.cpp |   4 +-
 xs/src/libslic3r/Model.cpp              |   1 +
 xs/src/libslic3r/Polyline.hpp           |   2 +
 xs/src/libslic3r/TriangleMesh.cpp       | 240 ++++++++++++++++--------
 xs/src/libslic3r/TriangleMesh.hpp       |  27 ++-
 5 files changed, 188 insertions(+), 86 deletions(-)

diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
index aa0e0f6b0..aa52856ae 100644
--- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
         Polylines paths;
         {
             Polylines p;
-            for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
-                p.push_back((Polyline)(*it));
+            for (Polygon &poly : polygons)
+                p.emplace_back(poly.points);
             paths = intersection_pl(p, to_polygons(expolygon));
         }
 
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index bceeea258..f08736f4e 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -613,6 +613,7 @@ const BoundingBoxf3& ModelObject::bounding_box() const
         BoundingBoxf3 raw_bbox;
         for (const ModelVolume *v : this->volumes)
             if (! v->modifier)
+                // mesh.bounding_box() returns a cached value.
                 raw_bbox.merge(v->mesh.bounding_box());
         BoundingBoxf3 bb;
         for (const ModelInstance *i : this->instances)
diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp
index b64743d84..123ca5d2c 100644
--- a/xs/src/libslic3r/Polyline.hpp
+++ b/xs/src/libslic3r/Polyline.hpp
@@ -19,6 +19,8 @@ public:
     Polyline() {};
     Polyline(const Polyline &other) : MultiPoint(other.points) {}
     Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
+    explicit Polyline(const Points &points) : MultiPoint(points) {}
+    explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
     Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
     Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
 	static Polyline new_scale(std::vector<Pointf> points) {
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 45e4b6f5d..b83e0dae6 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -696,8 +696,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
     }
 }
 
-void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
+void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
 {
     BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
 
@@ -800,35 +799,37 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
     for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
         std::vector<float>::size_type layer_idx = it - z.begin();
         IntersectionLine il;
-        if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) {
+        if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
             boost::lock_guard<boost::mutex> l(*lines_mutex);
             if (il.edge_type == feHorizontal) {
-                // Insert all three edges of the face.
-                const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
-                const bool reverse  = this->mesh->stl.facet_start[facet_idx].normal.z < 0;
-                for (int j = 0; j < 3; ++ j) {
-                    int               a_id     = vertices[j % 3];
-                    int               b_id     = vertices[(j+1) % 3];
-                    if (reverse)
-                        std::swap(a_id, b_id);
-                    const stl_vertex *a = &this->v_scaled_shared[a_id];
-                    const stl_vertex *b = &this->v_scaled_shared[b_id];
-                    il.a.x    = a->x;
-                    il.a.y    = a->y;
-                    il.b.x    = b->x;
-                    il.b.y    = b->y;
-                    il.a_id   = a_id;
-                    il.b_id   = b_id;
-                    (*lines)[layer_idx].push_back(il);
-                }
+                // Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face
+                // (they may not have a nighbor, or their neighbor is vertical)
+                const int *vertices  = this->mesh->stl.v_indices[facet_idx].vertex;
+                const bool reverse   = this->mesh->stl.facet_start[facet_idx].normal.z < 0;
+                uint32_t   edge_mask = IntersectionLine::EDGE0;
+                for (int j = 0; j < 3; ++ j, edge_mask <<= 1)
+                    if (il.flags & edge_mask) {
+                        int a_id = vertices[j % 3];
+                        int b_id = vertices[(j+1) % 3];
+                        if (reverse)
+                            std::swap(a_id, b_id);
+                        const stl_vertex *a = &this->v_scaled_shared[a_id];
+                        const stl_vertex *b = &this->v_scaled_shared[b_id];
+                        il.a.x    = a->x;
+                        il.a.y    = a->y;
+                        il.b.x    = b->x;
+                        il.b.y    = b->y;
+                        il.a_id   = a_id;
+                        il.b_id   = b_id;
+                        (*lines)[layer_idx].emplace_back(il);
+                    }
             } else
-                (*lines)[layer_idx].push_back(il);
+                (*lines)[layer_idx].emplace_back(il);
         }
     }
 }
 
-void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
+void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
 {
     std::vector<Polygons> layers_p;
     this->slice(z, &layers_p);
@@ -848,8 +849,18 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
 	BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
 }
 
+static inline float cross_product(const stl_vertex *a, const stl_vertex *b, const stl_vertex *c)
+{
+    float v1_x = b->x - a->x;
+    float v1_y = b->y - a->y;
+    float v2_x = c->x - a->x;
+    float v2_y = c->y - a->y;
+    float dir = (b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x);
+    return dir;
+}
+
 // Return true, if the facet has been sliced and line_out has been filled.
-bool TriangleMeshSlicer::slice_facet(
+TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
     float slice_z, const stl_facet &facet, const int facet_idx,
     const float min_z, const float max_z, 
     IntersectionLine *line_out) const
@@ -877,22 +888,61 @@ bool TriangleMeshSlicer::slice_facet(
             const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
             const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
             const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
+            // We may ignore this edge for slicing purposes, but we may still use it for object cutting.
+            FacetSliceType    result = Slicing;
+            const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
             if (min_z == max_z) {
                 // All three vertices are aligned with slice_z.
                 line_out->edge_type = feHorizontal;
+                // Mark neighbor edges, which do not have a neighbor.
+                uint32_t edges = 0;
+                uint32_t mask  = IntersectionLine::EDGE0;
+                for (int nbr_idx = 2; nbr_idx != 5; ++ nbr_idx, mask <<= 1)
+                    // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
+                    // opposite face, add it to the edges to process when slicing.
+                    if (nbr.neighbor[nbr_idx % 3] == -1)
+                        // Mark this edge.
+                        edges |= mask;
+                // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
+                result = (edges == 0) ? Cutting : Slicing;
+                line_out->flags |= edges;
                 if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) {
                     // If normal points downwards this is a bottom horizontal facet so we reverse its point order.
                     std::swap(a, b);
                     std::swap(a_id, b_id);
                 }
-            } else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) {
-                // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
-                line_out->edge_type = feTop;
-                std::swap(a, b);
-                std::swap(a_id, b_id);
             } else {
-                // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
-                line_out->edge_type = feBottom;
+                // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
+                int  nbr_idx     = (j + 2) % 3;
+                int  nbr_face    = nbr.neighbor[nbr_idx];
+                // Is the third vertex below the cutting plane?
+                bool third_below = v0.z < slice_z || v1.z < slice_z || v2.z < slice_z;
+                // Is this a concave corner?
+                if (nbr_face == -1) {
+#ifdef _DEBUG
+                    printf("Face has no neighbor!\n");
+#endif
+                } else {
+                    int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
+                    const stl_vertex *c = &this->v_scaled_shared[idx_vertex_opposite];
+                    if (c->z > slice_z) {
+                        // If an edge resides on a cutting plane, and none of the two triangles are coplanar with the cutting plane,
+                        // igore the lower triangle.
+                        if (third_below)
+                            result = Cutting;
+                    } else if (c->z == slice_z) {
+                        // A vertical face shares edge with a horizontal face. Verify, whether the shared corner is convex or concave.
+                        float dir = cross_product(a, b, c);
+                        if (third_below ? (dir < 0.) : (dir > 0.))
+                            result = Cutting;
+                    }
+                }
+                if (third_below) {
+                    line_out->edge_type = feTop;
+                    std::swap(a, b);
+                    std::swap(a_id, b_id);
+                } else
+                    line_out->edge_type = feBottom;
             }
             line_out->a.x    = a->x;
             line_out->a.y    = a->y;
@@ -900,7 +950,7 @@ bool TriangleMeshSlicer::slice_facet(
             line_out->b.y    = b->y;
             line_out->a_id   = a_id;
             line_out->b_id   = b_id;
-            return true;
+            return result;
         }
 
         if (a->z == slice_z) {
@@ -935,7 +985,7 @@ bool TriangleMeshSlicer::slice_facet(
         assert(num_points == 2 || num_points == 3);
         if (num_points < 3)
             // This triangle touches the cutting plane with a single vertex. Ignore it.
-            return false;
+            return NoSlice;
         // Erase one of the duplicate points.
         -- num_points;
         for (int i = points_on_layer[1]; i < num_points; ++ i)
@@ -945,52 +995,86 @@ bool TriangleMeshSlicer::slice_facet(
     // Facets must intersect each plane 0 or 2 times.
     assert(num_points == 0 || num_points == 2);
     if (num_points == 2) {
-        line_out->edge_type  = feNone;
+        line_out->edge_type  = feGeneral;
         line_out->a          = (Point)points[1];
         line_out->b          = (Point)points[0];
         line_out->a_id       = points[1].point_id;
         line_out->b_id       = points[0].point_id;
         line_out->edge_a_id  = points[1].edge_id;
         line_out->edge_b_id  = points[0].edge_id;
-        return true;
+        // General slicing position, use the segment for both slicing and object cutting.
+        return Slicing;
+    }
+    return NoSlice;
+}
+
+//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
+// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
+// So the following code makes only sense now to handle degenerate meshes with more than two faces
+// sharing a single edge.
+static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
+{
+    std::vector<IntersectionLine*> by_vertex_pair;
+    by_vertex_pair.reserve(lines.size());
+    for (IntersectionLine& line : lines)
+        if (line.edge_type != feGeneral && line.a_id != -1)
+            // This is a face edge. Check whether there is its neighbor stored in lines.
+            by_vertex_pair.emplace_back(&line);
+    auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
+        // Sort vertices of l1, l2 lexicographically
+        int l1a = l1->a_id;
+        int l1b = l1->b_id;
+        int l2a = l2->a_id;
+        int l2b = l2->b_id;
+        if (l1a > l1b)
+            std::swap(l1a, l1b);
+        if (l2a > l2b)
+            std::swap(l2a, l2b);
+        // Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
+        return l1a < l2a || (l1a == l2a && l1b < l2b);
+    };
+    std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
+    for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
+        IntersectionLine &l1 = **line;
+        if (! l1.skip()) {
+            // Iterate as long as line and line2 edges share the same end points.
+            for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
+                // Lines must share the end points.
+                assert(! edges_lower_sorted(*line, *line2));
+                assert(! edges_lower_sorted(*line2, *line));
+                IntersectionLine &l2 = **line2;
+                if (l2.skip())
+                    continue;
+                if (l1.a_id == l2.a_id) {
+                    assert(l1.b_id == l2.b_id);
+                    l2.set_skip();
+                    // If they are both oriented upwards or downwards (like a 'V'),
+                    // then we can remove both edges from this layer since it won't 
+                    // affect the sliced shape.
+                    // If one of them is oriented upwards and the other is oriented
+                    // downwards, let's only keep one of them (it doesn't matter which
+                    // one since all 'top' lines were reversed at slicing).
+                    if (l1.edge_type == l2.edge_type) {
+                        l1.set_skip();
+                        break;
+                    }
+                } else {
+                    assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
+                    // If this edge joins two horizontal facets, remove both of them.
+                    if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
+                        l1.set_skip();
+                        l2.set_skip();
+                        break;
+                    }
+                }
+            }
+        }
     }
-    return false;
 }
 
 void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
 {
-    // Remove tangent edges.
-    //FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane.
-    for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line)
-        if (! line->skip && line->edge_type != feNone) {
-            // This line is af facet edge. There may be a duplicate line with the same end vertices.
-            // If the line is is an edge connecting two facets, find another facet edge
-            // having the same endpoints but in reverse order.
-            for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2)
-                if (! line2->skip && line2->edge_type != feNone) {
-                    // Are these facets adjacent? (sharing a common edge on this layer)
-                    if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
-                        line2->skip = true;
-                        /* if they are both oriented upwards or downwards (like a 'V')
-                           then we can remove both edges from this layer since it won't 
-                           affect the sliced shape */
-                        /* if one of them is oriented upwards and the other is oriented
-                           downwards, let's only keep one of them (it doesn't matter which
-                           one since all 'top' lines were reversed at slicing) */
-                        if (line->edge_type == line2->edge_type) {
-                            line->skip = true;
-                            break;
-                        }
-                    } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
-                        /* if this edge joins two horizontal facets, remove both of them */
-                        if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
-                            line->skip = true;
-                            line2->skip = true;
-                            break;
-                        }
-                    }
-                }
-        }
+    remove_tangent_edges(lines);
 
     struct OpenPolyline {
         OpenPolyline() {};
@@ -1013,7 +1097,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
         by_edge_a_id.reserve(lines.size());
         by_a_id.reserve(lines.size());
         for (IntersectionLine &line : lines) {
-            if (! line.skip) {
+            if (! line.skip()) {
                 if (line.edge_a_id != -1)
                     by_edge_a_id.emplace_back(&line);
                 if (line.a_id != -1)
@@ -1030,13 +1114,13 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
             // take first spare line and start a new loop
             IntersectionLine *first_line = nullptr;
             for (; it_line_seed != lines.end(); ++ it_line_seed)
-                if (! it_line_seed->skip) {
+                if (! it_line_seed->skip()) {
                     first_line = &(*it_line_seed ++);
                     break;
                 }
             if (first_line == nullptr)
                 break;
-            first_line->skip = true;
+            first_line->set_skip();
             Points loop_pts;
             loop_pts.emplace_back(first_line->a);
             IntersectionLine *last_line = first_line;
@@ -1057,7 +1141,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                     if (it_begin != by_edge_a_id.end()) {
                         auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
                         for (auto it_line = it_begin; it_line != it_end; ++ it_line)
-                            if (! (*it_line)->skip) {
+                            if (! (*it_line)->skip()) {
                                 next_line = *it_line;
                                 break;
                             }
@@ -1069,7 +1153,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                     if (it_begin != by_a_id.end()) {
                         auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
                         for (auto it_line = it_begin; it_line != it_end; ++ it_line)
-                            if (! (*it_line)->skip) {
+                            if (! (*it_line)->skip()) {
                                 next_line = *it_line;
                                 break;
                             }
@@ -1100,7 +1184,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                 */
                 loop_pts.emplace_back(next_line->a);
                 last_line = next_line;
-                next_line->skip = true;
+                next_line->set_skip();
             }
         }
     }
@@ -1192,8 +1276,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
                 if ((ip1.edge_id  != -1 && ip1.edge_id  == ip2.edge_id) ||
                     (ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
                     // The current loop is complete. Add it to the output.
-                    assert(opl.points.front().point_id == opl.points.back().point_id);
-                    assert(opl.points.front().edge_id  == opl.points.back().edge_id);
+                    //assert(opl.points.front().point_id == opl.points.back().point_id);
+                    //assert(opl.points.front().edge_id  == opl.points.back().edge_id);
                     // Remove the duplicate last point.
                     opl.points.pop_back();
                     if (opl.points.size() >= 3) {
@@ -1393,7 +1477,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
         
         // intersect facet with cutting plane
         IntersectionLine line;
-        if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
+        if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
             // Save intersection lines for generating correct triangulations.
             if (line.edge_type == feTop) {
                 lower_lines.push_back(line);
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index c700784a5..3714f5e9e 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -76,7 +76,7 @@ private:
 
 enum FacetEdgeType { 
     // A general case, the cutting plane intersect a face at two different edges.
-    feNone,
+    feGeneral,
     // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
     feTop,
     // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
@@ -110,6 +110,11 @@ public:
 class IntersectionLine : public Line
 {
 public:
+    IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
+
+    bool skip() const { return (this->flags & SKIP) != 0; }
+    void set_skip() { this->flags |= SKIP; }
+    
     // Inherits Point a, b
     // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
     // Vertex indices of the line end points.
@@ -118,11 +123,16 @@ public:
     // Source mesh edges of the line end points.
     int             edge_a_id;
     int             edge_b_id;
-    // feNone, feTop, feBottom, feHorizontal
+    // feGeneral, feTop, feBottom, feHorizontal
     FacetEdgeType   edge_type;
-    // Used by TriangleMeshSlicer::make_loops() to skip duplicate edges.
-    bool            skip;
-    IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {};
+    // Used by TriangleMeshSlicer::slice() to skip duplicate edges.
+    enum {
+        EDGE0   = 1,
+        EDGE1   = 2,
+        EDGE2   = 4,
+        SKIP    = 8,
+    };
+    uint32_t        flags;
 };
 typedef std::vector<IntersectionLine> IntersectionLines;
 typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
@@ -133,7 +143,12 @@ public:
     TriangleMeshSlicer(TriangleMesh* _mesh);
     void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
     void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
-    bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
+    enum FacetSliceType {
+        NoSlice = 0,
+        Slicing = 1,
+        Cutting = 2
+    };
+    FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
         const float min_z, const float max_z, IntersectionLine *line_out) const;
     void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
     

From 0ea45576327fc974655dccc2234e269f6ac60bb8 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 9 Aug 2018 21:15:49 +0200
Subject: [PATCH 06/22] Improved accuracy of slicing (triangle cutting) code,
 improved debugging outputs and asserts of the slicing code. Disabled
 detection of concave corners with horizontal faces, as too often there were
 found models with badly triangulated faces, see for example GH issue #895.

---
 xs/src/libslic3r/TriangleMesh.cpp | 97 ++++++++++++++++++++-----------
 xs/src/libslic3r/Utils.hpp        |  1 +
 xs/src/libslic3r/utils.cpp        | 10 ++++
 xs/xsp/XS.xsp                     |  5 ++
 4 files changed, 78 insertions(+), 35 deletions(-)

diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index b83e0dae6..0941c11ef 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -15,16 +15,20 @@
 
 #include <tbb/parallel_for.h>
 
+// for SLIC3R_DEBUG_SLICE_PROCESSING
+#include "libslic3r.h"
+
 #if 0
     #define DEBUG
     #define _DEBUG
     #undef NDEBUG
+    #define SLIC3R_DEBUG
+// #define SLIC3R_TRIANGLEMESH_DEBUG
 #endif
 
 #include <assert.h>
 
-#ifdef SLIC3R_DEBUG
-// #define SLIC3R_TRIANGLEMESH_DEBUG
+#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
 #include "SVG.hpp"
 #endif
 
@@ -758,8 +762,22 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
     {
         static int iRun = 0;
         for (size_t i = 0; i < z.size(); ++ i) {
-            Polygons &polygons = (*layers)[i];
-            SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
+            Polygons  &polygons   = (*layers)[i];
+            ExPolygons expolygons = union_ex(polygons, true);
+            SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
+            {
+                BoundingBox bbox;
+                for (const IntersectionLine &l : lines[i]) {
+                    bbox.merge(l.a);
+                    bbox.merge(l.b);
+                }
+                SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
+                svg.draw(expolygons);
+                for (const IntersectionLine &l : lines[i])
+                    svg.draw(l, "red", 0);
+                svg.draw_outline(expolygons, "black", "blue", 0);
+                svg.Close();
+            }
             for (Polygon &poly : polygons) {
                 for (size_t i = 1; i < poly.points.size(); ++ i)
                     assert(poly.points[i-1] != poly.points[i]);
@@ -821,6 +839,7 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
                         il.b.y    = b->y;
                         il.a_id   = a_id;
                         il.b_id   = b_id;
+                        assert(il.a != il.b);
                         (*lines)[layer_idx].emplace_back(il);
                     }
             } else
@@ -849,16 +868,6 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
 	BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
 }
 
-static inline float cross_product(const stl_vertex *a, const stl_vertex *b, const stl_vertex *c)
-{
-    float v1_x = b->x - a->x;
-    float v1_y = b->y - a->y;
-    float v2_x = c->x - a->x;
-    float v2_y = c->y - a->y;
-    float dir = (b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x);
-    return dir;
-}
-
 // Return true, if the facet has been sliced and line_out has been filled.
 TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
     float slice_z, const stl_facet &facet, const int facet_idx,
@@ -873,10 +882,10 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
     // Reorder vertices so that the first one is the one with lowest Z.
     // This is needed to get all intersection lines in a consistent order
     // (external on the right of the line)
-    int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
-    for (int j = i; j - i < 3; ++ j) {  // loop through facet edges
+	const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
+	int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
+	for (int j = i; j - i < 3; ++j) {  // loop through facet edges
         int               edge_id  = this->facets_edges[facet_idx * 3 + (j % 3)];
-        const int        *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
         int               a_id     = vertices[j % 3];
         int               b_id     = vertices[(j+1) % 3];
         const stl_vertex *a = &this->v_scaled_shared[a_id];
@@ -888,6 +897,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
             const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
             const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
             const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
+            const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
             // We may ignore this edge for slicing purposes, but we may still use it for object cutting.
             FacetSliceType    result = Slicing;
             const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
@@ -897,23 +907,23 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
                 // Mark neighbor edges, which do not have a neighbor.
                 uint32_t edges = 0;
                 uint32_t mask  = IntersectionLine::EDGE0;
-                for (int nbr_idx = 2; nbr_idx != 5; ++ nbr_idx, mask <<= 1)
+                for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx, mask <<= 1)
                     // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
                     // opposite face, add it to the edges to process when slicing.
-                    if (nbr.neighbor[nbr_idx % 3] == -1)
+                    if (nbr.neighbor[nbr_idx] == -1)
                         // Mark this edge.
                         edges |= mask;
                 // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
                 result = (edges == 0) ? Cutting : Slicing;
                 line_out->flags |= edges;
-                if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) {
+                if (normal.z < 0) {
                     // If normal points downwards this is a bottom horizontal facet so we reverse its point order.
                     std::swap(a, b);
                     std::swap(a_id, b_id);
                 }
             } else {
                 // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
-                int  nbr_idx     = (j + 2) % 3;
+                int  nbr_idx     = j % 3;
                 int  nbr_face    = nbr.neighbor[nbr_idx];
                 // Is the third vertex below the cutting plane?
                 bool third_below = v0.z < slice_z || v1.z < slice_z || v2.z < slice_z;
@@ -923,19 +933,25 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
                     printf("Face has no neighbor!\n");
 #endif
                 } else {
+                    assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
+                    assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
                     int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
                     const stl_vertex *c = &this->v_scaled_shared[idx_vertex_opposite];
-                    if (c->z > slice_z) {
-                        // If an edge resides on a cutting plane, and none of the two triangles are coplanar with the cutting plane,
-                        // igore the lower triangle.
-                        if (third_below)
-                            result = Cutting;
-                    } else if (c->z == slice_z) {
-                        // A vertical face shares edge with a horizontal face. Verify, whether the shared corner is convex or concave.
-                        float dir = cross_product(a, b, c);
-                        if (third_below ? (dir < 0.) : (dir > 0.))
-                            result = Cutting;
-                    }
+//					double side = double(normal.x) * (double(c->x) - double(a->x)) + double(normal.y) * (double(c->y) - double(a->y));
+//					assert(c->z != slice_z || side != 0.);
+//                  double normal_nbr = (double(c->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c->y) - double(a->y)) * (double(b->x) - double(a->x));
+					result = 
+						(c->z == slice_z) ?
+							// A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
+                            // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
+                            // and leth the code downstream hopefully handle it.
+                            Slicing :
+                            // Failing tests: Ignore concave corners for slicing.
+                            //   (((normal_nbr < 0) == third_below) ? Cutting : Slicing) :
+                            // or
+                            //   (((this->mesh->stl.facet_start[nbr_face].normal.z < 0) == third_below) ? Cutting : Slicing) :
+                            // For a pair of faces touching exactly at the cutting plane, ignore the face with a higher index.
+                            (facet_idx < nbr_face) ? Slicing : Cutting;
                 }
                 if (third_below) {
                     line_out->edge_type = feTop;
@@ -950,6 +966,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
             line_out->b.y    = b->y;
             line_out->a_id   = a_id;
             line_out->b_id   = b_id;
+            assert(line_out->a != line_out->b);
             return result;
         }
 
@@ -970,8 +987,9 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
         } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) {
             // A general case. The face edge intersects the cutting plane. Calculate the intersection point.
             IntersectionPoint &point = points[num_points ++];
-            point.x         = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z);
-            point.y         = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z);
+            double t        = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z));
+            point.x         = float(double(b->x) + (double(a->x) - double(b->x)) * t);
+            point.y         = float(double(b->y) + (double(a->y) - double(b->y)) * t);
             point.edge_id   = edge_id;
         }
     }
@@ -1003,7 +1021,11 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
         line_out->edge_a_id  = points[1].edge_id;
         line_out->edge_b_id  = points[0].edge_id;
         // General slicing position, use the segment for both slicing and object cutting.
-        return Slicing;
+        // In a degenerate case where a plane cuts the triangle very close to its vertex, it is possible, that 
+        // a zero length edge is created. In that case the zero length edge could be safely ignored
+        // as the polyline will still be connected, because both the sliced edges of the triangle will be 
+        // sliced the same way at the neighbor triangles.
+		return (line_out->a == line_out->b) ? NoSlice : Slicing;
     }
     return NoSlice;
 }
@@ -1074,6 +1096,11 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
 
 void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
 {
+#ifdef _DEBUG
+    for (const Line &l : lines_src)
+        assert(l.a != l.b);
+#endif /* _DEBUG */
+
     remove_tangent_edges(lines);
 
     struct OpenPolyline {
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index 349222854..fd63d412a 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -9,6 +9,7 @@ namespace Slic3r {
 
 extern void set_logging_level(unsigned int level);
 extern void trace(unsigned int level, const char *message);
+extern void disable_multi_threading();
 
 // Set a path with GUI resource files.
 void set_var_dir(const std::string &path);
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 55164bbdd..4ca4f69fd 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -24,6 +24,8 @@
 #include <boost/nowide/integration/filesystem.hpp>
 #include <boost/nowide/convert.hpp>
 
+#include <tbb/task_scheduler_init.h>
+
 namespace Slic3r {
 
 static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
@@ -82,6 +84,14 @@ void trace(unsigned int level, const char *message)
         (::boost::log::keywords::severity = severity)) << message;
 }
 
+void disable_multi_threading()
+{
+    // Disable parallelization so the Shiny profiler works
+    static tbb::task_scheduler_init *tbb_init = nullptr;
+    if (tbb_init == nullptr)
+        tbb_init = new tbb::task_scheduler_init(1);
+}
+
 static std::string g_var_dir;
 
 void set_var_dir(const std::string &dir)
diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp
index e900532aa..04969a7f9 100644
--- a/xs/xsp/XS.xsp
+++ b/xs/xsp/XS.xsp
@@ -48,6 +48,11 @@ trace(level, message)
     CODE:
         Slic3r::trace(level, message);
 
+void
+disable_multi_threading()
+    CODE:
+        Slic3r::disable_multi_threading();
+
 void
 set_var_dir(dir)
     char  *dir;

From b67f32a94d83619f7b2056c22a0e55233238160f Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 10 Aug 2018 14:10:28 +0200
Subject: [PATCH 07/22] Slicing improvement for slicing degenerated meshes: Add
 edges of parallel folded horizontal faces into the output contours, but
 ignore them when taking seeds for contour extraction. FIXME: Single vertices
 touching a plane are not handled correctly, they create zero length edges.

---
 xs/src/libslic3r/TriangleMesh.cpp | 145 +++++++++++++++++++++---------
 xs/src/libslic3r/TriangleMesh.hpp |  18 +++-
 2 files changed, 118 insertions(+), 45 deletions(-)

diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 0941c11ef..be554aef3 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -223,7 +223,6 @@ TriangleMesh::repair() {
     BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
 }
 
-
 float TriangleMesh::volume()
 {
     if (this->stl.stats.volume == -1) 
@@ -427,7 +426,7 @@ bool TriangleMesh::has_multiple_patches() const
         facet_visited[facet_idx] = true;
         for (int j = 0; j < 3; ++ j) {
             int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
-            if (! facet_visited[neighbor_idx])
+            if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
                 facet_queue[facet_queue_cnt ++] = neighbor_idx;
         }
     }
@@ -479,8 +478,7 @@ size_t TriangleMesh::number_of_patches() const
     return num_bodies;
 }
 
-TriangleMeshPtrs
-TriangleMesh::split() const
+TriangleMeshPtrs TriangleMesh::split() const
 {
     TriangleMeshPtrs meshes;
     std::set<int> seen_facets;
@@ -532,8 +530,7 @@ TriangleMesh::split() const
     return meshes;
 }
 
-void
-TriangleMesh::merge(const TriangleMesh &mesh)
+void TriangleMesh::merge(const TriangleMesh &mesh)
 {
     // reset stats and metadata
     int number_of_facets = this->stl.stats.number_of_facets;
@@ -587,8 +584,7 @@ Polygon TriangleMesh::convex_hull()
     return Slic3r::Geometry::convex_hull(pp);
 }
 
-BoundingBoxf3
-TriangleMesh::bounding_box() const
+BoundingBoxf3 TriangleMesh::bounding_box() const
 {
     BoundingBoxf3 bb;
     bb.defined = true;
@@ -601,8 +597,7 @@ TriangleMesh::bounding_box() const
     return bb;
 }
 
-void
-TriangleMesh::require_shared_vertices()
+void TriangleMesh::require_shared_vertices()
 {
     BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
     if (!this->repaired) 
@@ -611,10 +606,23 @@ TriangleMesh::require_shared_vertices()
         BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
         stl_generate_shared_vertices(&(this->stl));
     }
+#ifdef _DEBUG
+	// Verify validity of neighborship data.
+	for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
+		const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
+		const int *vertices = stl.v_indices[facet_idx].vertex;
+		for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
+			int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
+			if (nbr_face != -1) {
+				assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
+				assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
+			}
+		}
+	}
+#endif /* _DEBUG */
     BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
 }
 
-
 TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : 
     mesh(_mesh)
 {
@@ -778,11 +786,14 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
                 svg.draw_outline(expolygons, "black", "blue", 0);
                 svg.Close();
             }
+#if 0
+//FIXME the slice_facet() creates zero length edges.
             for (Polygon &poly : polygons) {
                 for (size_t i = 1; i < poly.points.size(); ++ i)
                     assert(poly.points[i-1] != poly.points[i]);
                 assert(poly.points.front() != poly.points.back());
             }
+#endif
         }
         ++ iRun;
     }
@@ -798,21 +809,21 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
     const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
     const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
     
-    #ifdef SLIC3R_DEBUG
+    #ifdef SLIC3R_TRIANGLEMESH_DEBUG
     printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
         facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
         facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
         facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
     printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
-    #endif
+    #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
     
     // find layer extents
     std::vector<float>::const_iterator min_layer, max_layer;
     min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
     max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
-    #ifdef SLIC3R_DEBUG
+    #ifdef SLIC3R_TRIANGLEMESH_DEBUG
     printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
-    #endif
+    #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
     
     for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
         std::vector<float>::size_type layer_idx = it - z.begin();
@@ -824,9 +835,8 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
                 // (they may not have a nighbor, or their neighbor is vertical)
                 const int *vertices  = this->mesh->stl.v_indices[facet_idx].vertex;
                 const bool reverse   = this->mesh->stl.facet_start[facet_idx].normal.z < 0;
-                uint32_t   edge_mask = IntersectionLine::EDGE0;
-                for (int j = 0; j < 3; ++ j, edge_mask <<= 1)
-                    if (il.flags & edge_mask) {
+                for (int j = 0; j < 3; ++ j)
+                    if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) {
                         int a_id = vertices[j % 3];
                         int b_id = vertices[(j+1) % 3];
                         if (reverse)
@@ -840,6 +850,8 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
                         il.a_id   = a_id;
                         il.b_id   = b_id;
                         assert(il.a != il.b);
+                        // This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces.
+                        il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0);
                         (*lines)[layer_idx].emplace_back(il);
                     }
             } else
@@ -906,13 +918,45 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
                 line_out->edge_type = feHorizontal;
                 // Mark neighbor edges, which do not have a neighbor.
                 uint32_t edges = 0;
-                uint32_t mask  = IntersectionLine::EDGE0;
-                for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx, mask <<= 1)
+                for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) {
                     // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
                     // opposite face, add it to the edges to process when slicing.
-                    if (nbr.neighbor[nbr_idx] == -1)
-                        // Mark this edge.
-                        edges |= mask;
+					if (nbr.neighbor[nbr_idx] == -1) {
+						// Mark this edge to be added to the slice.
+						edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx);
+					}
+#if 1
+                     else if (normal.z > 0) {
+                        // Produce edges for opposite faced overlapping horizontal faces aka folds.
+                        // This method often produces connecting lines (noise) at the cutting plane.
+                        // Produce the edges for the top facing face of the pair of top / bottom facing faces.
+
+                        // Index of a neighbor face.
+                        const int  nbr_face     = nbr.neighbor[nbr_idx];
+                        const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex;
+                        int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]];
+                        const stl_vertex *c2 = &this->v_scaled_shared[idx_vertex_opposite];
+                        if (c2->z == slice_z) {
+                            // Edge shared by facet_idx and nbr_face.
+                            int               a_id      = vertices[nbr_idx];
+                            int               b_id      = vertices[(nbr_idx + 1) % 3];
+                            int               c1_id     = vertices[(nbr_idx + 2) % 3];
+                            const stl_vertex *a         = &this->v_scaled_shared[a_id];
+                            const stl_vertex *b         = &this->v_scaled_shared[b_id];
+                            const stl_vertex *c1        = &this->v_scaled_shared[c1_id];
+                            // Verify that the two neighbor faces share a common edge.
+                            assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
+                            assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
+                            double n1 = (double(c1->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c1->y) - double(a->y)) * (double(b->x) - double(a->x));
+                            double n2 = (double(c2->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c2->y) - double(a->y)) * (double(b->x) - double(a->x));
+                            if (n1 * n2 > 0)
+                                // The two faces overlap. This indicates an invalid mesh geometry (non-manifold),
+                                // but these are the real world objects, and leaving out these edges leads to missing contours.
+                                edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx);
+                         }
+                    }
+#endif
+                }
                 // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
                 result = (edges == 0) ? Cutting : Slicing;
                 line_out->flags |= edges;
@@ -937,21 +981,31 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
                     assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
                     int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
                     const stl_vertex *c = &this->v_scaled_shared[idx_vertex_opposite];
-//					double side = double(normal.x) * (double(c->x) - double(a->x)) + double(normal.y) * (double(c->y) - double(a->y));
-//					assert(c->z != slice_z || side != 0.);
-//                  double normal_nbr = (double(c->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c->y) - double(a->y)) * (double(b->x) - double(a->x));
-					result = 
-						(c->z == slice_z) ?
-							// A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
-                            // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
-                            // and leth the code downstream hopefully handle it.
-                            Slicing :
-                            // Failing tests: Ignore concave corners for slicing.
-                            //   (((normal_nbr < 0) == third_below) ? Cutting : Slicing) :
-                            // or
-                            //   (((this->mesh->stl.facet_start[nbr_face].normal.z < 0) == third_below) ? Cutting : Slicing) :
-                            // For a pair of faces touching exactly at the cutting plane, ignore the face with a higher index.
-                            (facet_idx < nbr_face) ? Slicing : Cutting;
+                    if (c->z == slice_z) {
+                        double normal_nbr = (double(c->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c->y) - double(a->y)) * (double(b->x) - double(a->x));
+#if 0
+                        if ((normal_nbr < 0) == third_below) {
+                            printf("Flipped normal?\n");
+                        }
+#endif
+                        result =
+                                // A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
+                                // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
+                                // and leth the code downstream hopefully handle it.
+    #if 1
+                                // Ignore concave corners for slicing.
+                                // This method has the unfortunate property, that folds in a horizontal plane create concave corners,
+                                // leading to broken contours, if these concave corners are not replaced by edges of the folds, see above.
+                                   ((normal_nbr < 0) == third_below) ? Cutting : Slicing;
+    #else
+                                // Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing,
+                                // and rightly so.
+                                    Slicing;
+    #endif
+                    } else {
+                        // For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index.
+                        result = (facet_idx < nbr_face) ? Slicing : Cutting;
+                    }
                 }
                 if (third_below) {
                     line_out->edge_type = feTop;
@@ -1021,11 +1075,17 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
         line_out->edge_a_id  = points[1].edge_id;
         line_out->edge_b_id  = points[0].edge_id;
         // General slicing position, use the segment for both slicing and object cutting.
+#if 0
         // In a degenerate case where a plane cuts the triangle very close to its vertex, it is possible, that 
         // a zero length edge is created. In that case the zero length edge could be safely ignored
         // as the polyline will still be connected, because both the sliced edges of the triangle will be 
         // sliced the same way at the neighbor triangles.
 		return (line_out->a == line_out->b) ? NoSlice : Slicing;
+#else
+        // The chaining code primarily relies on the IDs of the edges.
+        // Even though there may be a zero length edge generated, it is still important,
+        return Slicing;
+#endif
     }
     return NoSlice;
 }
@@ -1096,8 +1156,10 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
 
 void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
 {
-#ifdef _DEBUG
-    for (const Line &l : lines_src)
+#if 0
+//FIXME the slice_facet() creates zero length edges.
+//#ifdef _DEBUG
+    for (const Line &l : lines)
         assert(l.a != l.b);
 #endif /* _DEBUG */
 
@@ -1141,7 +1203,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
             // take first spare line and start a new loop
             IntersectionLine *first_line = nullptr;
             for (; it_line_seed != lines.end(); ++ it_line_seed)
-                if (! it_line_seed->skip()) {
+                if (it_line_seed->is_seed_candidate()) {
+                //if (! it_line_seed->skip()) {
                     first_line = &(*it_line_seed ++);
                     break;
                 }
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index 3714f5e9e..89742fd4c 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -114,6 +114,9 @@ public:
 
     bool skip() const { return (this->flags & SKIP) != 0; }
     void set_skip() { this->flags |= SKIP; }
+
+    bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
+    void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
     
     // Inherits Point a, b
     // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
@@ -127,10 +130,17 @@ public:
     FacetEdgeType   edge_type;
     // Used by TriangleMeshSlicer::slice() to skip duplicate edges.
     enum {
-        EDGE0   = 1,
-        EDGE1   = 2,
-        EDGE2   = 4,
-        SKIP    = 8,
+        // Triangle edge added, because it has no neighbor.
+        EDGE0_NO_NEIGHBOR   = 0x001,
+        EDGE1_NO_NEIGHBOR   = 0x002,
+        EDGE2_NO_NEIGHBOR   = 0x004,
+        // Triangle edge added, because it makes a fold with another horizontal edge.
+        EDGE0_FOLD          = 0x010,
+        EDGE1_FOLD          = 0x020,
+        EDGE2_FOLD          = 0x040,
+        // The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
+        NO_SEED             = 0x100,
+        SKIP                = 0x200,
     };
     uint32_t        flags;
 };

From 13ce087606838d66a65f6f0a1db41ea71f8a623f Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 10 Aug 2018 17:37:09 +0200
Subject: [PATCH 08/22] Another improvement in robustness of mesh slicing.

---
 xs/src/libslic3r/TriangleMesh.cpp | 117 +++++++++++++++++-------------
 1 file changed, 68 insertions(+), 49 deletions(-)

diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index be554aef3..65a31fada 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -469,7 +469,7 @@ size_t TriangleMesh::number_of_patches() const
             facet_visited[facet_idx] = true;
             for (int j = 0; j < 3; ++ j) {
                 int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
-                if (! facet_visited[neighbor_idx])
+                if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
                     facet_queue[facet_queue_cnt ++] = neighbor_idx;
             }
         }
@@ -787,7 +787,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
                 svg.Close();
             }
 #if 0
-//FIXME the slice_facet() creates zero length edges.
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
             for (Polygon &poly : polygons) {
                 for (size_t i = 1; i < poly.points.size(); ++ i)
                     assert(poly.points[i-1] != poly.points[i]);
@@ -888,15 +888,14 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
 {
     IntersectionPoint points[3];
     size_t            num_points = 0;
-    size_t            points_on_layer[3];
-    size_t            num_points_on_layer = 0;
+    size_t            point_on_layer = size_t(-1);
     
     // Reorder vertices so that the first one is the one with lowest Z.
     // This is needed to get all intersection lines in a consistent order
     // (external on the right of the line)
 	const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
-	int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
-	for (int j = i; j - i < 3; ++j) {  // loop through facet edges
+    int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
+    for (int j = i; j - i < 3; ++j) {  // loop through facet edges
         int               edge_id  = this->facets_edges[facet_idx * 3 + (j % 3)];
         int               a_id     = vertices[j % 3];
         int               b_id     = vertices[(j+1) % 3];
@@ -1026,46 +1025,57 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
 
         if (a->z == slice_z) {
             // Only point a alings with the cutting plane.
-            points_on_layer[num_points_on_layer ++] = num_points;
-            IntersectionPoint &point = points[num_points ++];
-            point.x         = a->x;
-            point.y         = a->y;
-            point.point_id  = a_id;
+            if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+                point_on_layer = num_points;
+                IntersectionPoint &point = points[num_points ++];
+                point.x         = a->x;
+                point.y         = a->y;
+                point.point_id  = a_id;
+            }
         } else if (b->z == slice_z) {
             // Only point b alings with the cutting plane.
-            points_on_layer[num_points_on_layer ++] = num_points;
-            IntersectionPoint &point = points[num_points ++];
-            point.x         = b->x;
-            point.y         = b->y;
-            point.point_id  = b_id;
+            if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+                point_on_layer = num_points;
+                IntersectionPoint &point = points[num_points ++];
+                point.x         = b->x;
+                point.y         = b->y;
+                point.point_id  = b_id;
+            }
         } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) {
             // A general case. The face edge intersects the cutting plane. Calculate the intersection point.
-            IntersectionPoint &point = points[num_points ++];
-            double t        = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z));
-            point.x         = float(double(b->x) + (double(a->x) - double(b->x)) * t);
-            point.y         = float(double(b->y) + (double(a->y) - double(b->y)) * t);
-            point.edge_id   = edge_id;
+			assert(a_id != b_id);
+            // Sort the edge to give a consistent answer.
+			if (a_id > b_id) {
+				std::swap(a_id, b_id);
+				std::swap(a, b);
+			}
+            IntersectionPoint &point = points[num_points];
+            double t = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z));
+			if (t <= 0.) {
+				if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+					point.x = a->x;
+					point.y = a->y;
+					point_on_layer = num_points ++;
+					point.point_id = a_id;
+				}
+			} else if (t >= 1.) {
+				if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+					point.x = b->x;
+					point.y = b->y;
+					point_on_layer = num_points ++;
+					point.point_id = b_id;
+				}
+			} else {
+				point.x = coord_t(floor(double(b->x) + (double(a->x) - double(b->x)) * t + 0.5));
+				point.y = coord_t(floor(double(b->y) + (double(a->y) - double(b->y)) * t + 0.5));
+                point.edge_id = edge_id;
+                ++ num_points;
+            }
         }
     }
 
-    // We can't have only one point on layer because each vertex gets detected
-    // twice (once for each edge), and we can't have three points on layer,
-    // because we assume this code is not getting called for horizontal facets.
-    assert(num_points_on_layer == 0 || num_points_on_layer == 2);
-    if (num_points_on_layer > 0) {
-        assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
-        assert(num_points == 2 || num_points == 3);
-        if (num_points < 3)
-            // This triangle touches the cutting plane with a single vertex. Ignore it.
-            return NoSlice;
-        // Erase one of the duplicate points.
-        -- num_points;
-        for (int i = points_on_layer[1]; i < num_points; ++ i)
-            points[i] = points[i + 1];
-    }
-    
-    // Facets must intersect each plane 0 or 2 times.
-    assert(num_points == 0 || num_points == 2);
+    // Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
+    assert(num_points < 3);
     if (num_points == 2) {
         line_out->edge_type  = feGeneral;
         line_out->a          = (Point)points[1];
@@ -1074,18 +1084,27 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
         line_out->b_id       = points[0].point_id;
         line_out->edge_a_id  = points[1].edge_id;
         line_out->edge_b_id  = points[0].edge_id;
+        // Not a zero lenght edge.
+        //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+        //assert(line_out->a != line_out->b);
+        // The plane cuts at least one edge in a general position.
+        assert(line_out->a_id == -1 || line_out->b_id == -1);
+        assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
         // General slicing position, use the segment for both slicing and object cutting.
 #if 0
-        // In a degenerate case where a plane cuts the triangle very close to its vertex, it is possible, that 
-        // a zero length edge is created. In that case the zero length edge could be safely ignored
-        // as the polyline will still be connected, because both the sliced edges of the triangle will be 
-        // sliced the same way at the neighbor triangles.
-		return (line_out->a == line_out->b) ? NoSlice : Slicing;
-#else
-        // The chaining code primarily relies on the IDs of the edges.
-        // Even though there may be a zero length edge generated, it is still important,
-        return Slicing;
+        if (line_out->a_id != -1 && line_out->b_id != -1) {
+            // Solving a degenerate case, where both the intersections snapped to an edge.
+            // Correctly classify the face as below or above based on the position of the 3rd point.
+            int i = vertices[0];
+            if (i == line_out->a_id || i == line_out->b_id)
+                i = vertices[1];
+            if (i == line_out->a_id || i == line_out->b_id)
+                i = vertices[2];
+            assert(i != line_out->a_id && i != line_out->b_id);
+            line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
+        }
 #endif
+        return Slicing;
     }
     return NoSlice;
 }
@@ -1157,7 +1176,7 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
 void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
 {
 #if 0
-//FIXME the slice_facet() creates zero length edges.
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
 //#ifdef _DEBUG
     for (const Line &l : lines)
         assert(l.a != l.b);

From b0216b190af13ff82adc12eca0f8e813a1543e18 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 3 Sep 2018 10:15:40 +0200
Subject: [PATCH 09/22] Bugfix - extruder temperature was sometimes not
 correctly set on the wipe tower

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 42c06252b..caea1c5e7 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -525,6 +525,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
         ++ m_num_tool_changes;
     }
 
+    m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
+                            // in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
+
 	// Reset the extruder current to a normal value.
 	writer.set_extruder_trimpot(550)
 		  .feedrate(6000)

From e3de278afcc611a379f60dc5a4bcdfbadd471dca Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 5 Sep 2018 09:18:42 +0200
Subject: [PATCH 10/22] Another attempt to fix the temperature change issue

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index caea1c5e7..b6340e991 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -807,8 +807,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
           .load_move_x_advanced(old_x,         -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
           .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
           .resume_preview();
-
-    if (new_temperature != 0 && new_temperature != m_old_temperature ) { 	// Set the extruder temperature, but don't wait.
+    if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { 	// Set the extruder temperature, but don't wait.
+        // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
+        // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
 		writer.set_extruder_temp(new_temperature, false);
         m_old_temperature = new_temperature;
     }

From c83a5474f6f10832c98a56fe8c9717803041809d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 5 Sep 2018 15:35:35 +0200
Subject: [PATCH 11/22] Wipe tower extrusions are now accounted for in the
 filament consumption statistics

---
 lib/Slic3r/GUI/Plater.pm                    | 17 ++++++++-
 xs/src/libslic3r/GCode.cpp                  | 22 ++++++-----
 xs/src/libslic3r/GCode.hpp                  |  1 +
 xs/src/libslic3r/GCode/WipeTower.hpp        |  3 ++
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 41 ++++++++++++++-------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  7 ++++
 xs/src/libslic3r/Print.cpp                  |  2 +
 xs/src/libslic3r/Print.hpp                  |  3 +-
 xs/xsp/Print.xsp                            | 20 ++++++++++
 9 files changed, 91 insertions(+), 25 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index ee2689d16..7c9e9899e 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1648,13 +1648,26 @@ sub print_info_box_show {
         $print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
         my @info = (
             L("Used Filament (m)")
-                => sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+                => $self->{print}->total_wipe_tower_filament > 0 ?
+                       sprintf("%.2f  (%.2f %s + %.2f %s)" , $self->{print}->total_used_filament / 1000,
+                                                            ($self->{print}->total_used_filament - $self->{print}->total_wipe_tower_filament) / 1000,
+                                                             L("objects"),
+							     $self->{print}->total_wipe_tower_filament / 1000,
+                                                             L("wipe_tower")) :
+                       sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+
             L("Used Filament (mm³)")
                 => sprintf("%.2f" , $self->{print}->total_extruded_volume),
             L("Used Filament (g)"),
                 => sprintf("%.2f" , $self->{print}->total_weight),
             L("Cost"),
-                => sprintf("%.2f" , $self->{print}->total_cost),
+                => $self->{print}->total_wipe_tower_cost > 0 ?
+                       sprintf("%.2f  (%.2f %s + %.2f %s)" , $self->{print}->total_cost,
+                                                            ($self->{print}->total_cost - $self->{print}->total_wipe_tower_cost),
+                                                             L("objects"),
+							     $self->{print}->total_wipe_tower_cost,
+                                                             L("wipe_tower")) :
+                       sprintf("%.2f" , $self->{print}->total_cost),
             L("Estimated printing time (normal mode)")
                 => $self->{print}->estimated_normal_print_time,
             L("Estimated printing time (silent mode)")
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index b34ba5441..b6d17d56a 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -276,7 +276,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
 }
 
 
-
 std::string WipeTowerIntegration::prime(GCode &gcodegen)
 {
     assert(m_layer_idx == 0);
@@ -960,17 +959,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 
     // Get filament stats.
     print.filament_stats.clear();
-    print.total_used_filament    = 0.;
-    print.total_extruded_volume  = 0.;
-    print.total_weight           = 0.;
-    print.total_cost             = 0.;
+    print.total_used_filament       = 0.;
+    print.total_extruded_volume     = 0.;
+    print.total_weight              = 0.;
+    print.total_cost                = 0.;
+    print.total_wipe_tower_cost     = 0.;
+    print.total_wipe_tower_filament = 0.;
     print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
     print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
     for (const Extruder &extruder : m_writer.extruders()) {
-        double used_filament   = extruder.used_filament();
-        double extruded_volume = extruder.extruded_volume();
+        double used_filament   = extruder.used_filament() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f);
+        double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
         double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
         double filament_cost   = filament_weight * extruder.filament_cost()    * 0.001;
+
         print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
         _write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
         if (filament_weight > 0.) {
@@ -981,8 +983,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
                 _write_format(file, "; filament cost = %.1lf\n", filament_cost);
             }
         }
-        print.total_used_filament = print.total_used_filament + used_filament;
-        print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
+        print.total_used_filament += used_filament;
+        print.total_extruded_volume += extruded_volume;
+        print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
+        print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
     }
     _write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
     _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 4953c39fe..d319bee01 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -98,6 +98,7 @@ public:
     void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
     std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
     std::string finalize(GCode &gcodegen);
+    std::vector<float> used_filament_length() const;
 
 private:
     WipeTowerIntegration& operator=(const WipeTowerIntegration&);
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 9bf350328..e7cd8ea1a 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -155,6 +155,9 @@ public:
 	// the wipe tower has been completely covered by the tool change extrusions,
 	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
 	virtual bool 		     layer_finished() const = 0;
+
+    // Returns used filament length per extruder:
+    virtual std::vector<float> get_used_filament() const = 0;
 };
 
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 42c06252b..23a2bef9b 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -111,9 +111,10 @@ public:
 	const WipeTower::xy	 start_pos_rotated() const { return m_start_pos; }
 	const WipeTower::xy  pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
+    float                get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
 
 	// Extrude with an explicitely provided amount of extrusion.
-	Writer& extrude_explicit(float x, float y, float e, float f = 0.f) 
+	Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
 	{
 		if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
 			// Neither extrusion nor a travel move.
@@ -122,6 +123,8 @@ public:
 		float dx = x - m_current_pos.x;
 		float dy = y - m_current_pos.y;
 		double len = sqrt(dx*dx+dy*dy);
+        if (record_length)
+            m_used_filament_length += e;
 
 
 		// Now do the "internal rotation" with respect to the wipe tower center
@@ -162,8 +165,8 @@ public:
 		return *this;
 	}
 
-	Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f) 
-		{ return extrude_explicit(dest.x, dest.y, e, f); }
+	Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
+		{ return extrude_explicit(dest.x, dest.y, e, f, record_length); }
 
 	// Travel to a new XY position. f=0 means use the current value.
 	Writer& travel(float x, float y, float f = 0.f)
@@ -177,7 +180,7 @@ public:
 	{
 		float dx = x - m_current_pos.x;
 		float dy = y - m_current_pos.y;
-		return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f);
+		return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
 	}
 
 	Writer& extrude(const WipeTower::xy &dest, const float f = 0.f) 
@@ -259,8 +262,8 @@ public:
 	// extrude quickly amount e to x2 with feed f.
 	Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
 	{
-		extrude_explicit(x1, m_current_pos.y + dy, e0, f);
-		extrude_explicit(x2, m_current_pos.y, e);
+		extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
+		extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
 		return *this;
 	}
 
@@ -404,6 +407,7 @@ private:
 	float		  m_last_fan_speed = 0.f;
     int           current_temp = -1;
     const float   m_default_analyzer_line_width;
+    float         m_used_filament_length = 0.f;
 
 	std::string   set_format_X(float x)
 	{
@@ -537,6 +541,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	// so that tool_change() will know to extrude the wipe tower brim:
 	m_print_brim = true;
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = true;
 	result.print_z 	  	= this->m_z_pos;
@@ -632,6 +639,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
                   ";------------------\n"
                   "\n\n");
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = false;
 	result.print_z 	  	= this->m_z_pos;
@@ -683,6 +693,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
 
     m_print_brim = false;  // Mark the brim as extruded
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = false;
 	result.print_z 	  	= this->m_z_pos;
@@ -849,6 +862,9 @@ void WipeTowerPrusaMM::toolchange_Change(
 	const unsigned int 	new_tool, 
 	material_type 		new_material)
 {
+    // Ask the writer about how much of the old filament we consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	// Speed override for the material. Go slow for flex and soluble materials.
 	int speed_override;
 	switch (new_material) {
@@ -911,7 +927,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	const float& xl = cleaning_box.ld.x;
 	const float& xr = cleaning_box.rd.x;
 
-
 	// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
     //   the ordered volume, even if it means violating the box. This can later be removed and simply
     // wipe until the end of the assigned area.
@@ -926,7 +941,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
         m_left_to_right = !m_left_to_right;
     }
     
-
     // now the wiping itself:
 	for (int i = 0; true; ++i)	{
 		if (i!=0) {
@@ -935,7 +949,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 			else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
 			else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
 		}
-		
+
 		float traversed_x = writer.x();
 		if (m_left_to_right)
 			writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
@@ -1050,6 +1064,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 
     m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
 
+    // Ask our writer about how much material was consumed:
+    m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
 	ToolChangeResult result;
     result.priming      = false;
 	result.print_z 	  	= this->m_z_pos;
@@ -1167,6 +1184,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 
     m_layer_info = m_plan.begin();
     m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
+    for (auto& used : m_used_filament_length) // reset used filament stats
+        used = 0.f;
 
     std::vector<WipeTower::ToolChangeResult> layer_result;
 	for (auto layer : m_plan)
@@ -1208,9 +1227,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 	}
 }
 
-
-
-
 void WipeTowerPrusaMM::make_wipe_tower_square()
 {
 	const float width = m_wipe_tower_width - 3 * m_perimeter_width;
@@ -1234,7 +1250,6 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
 	plan_tower();				// propagates depth downwards again (width has changed)
 	for (auto& lay : m_plan)	// depths set, now the spacing
 		lay.extra_spacing = lay.depth / lay.toolchanges_depth();
-
 }
 
 
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 305dbc40a..964ee0039 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -94,6 +94,8 @@ public:
         m_filpar[idx].ramming_step_multiplicator /= 100;
         while (stream >> speed)
             m_filpar[idx].ramming_speed.push_back(speed);
+
+        m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
 	}
 
 
@@ -172,6 +174,8 @@ public:
 		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
 	}
 
+    virtual std::vector<float> get_used_filament() const { return m_used_filament_length; }
+
 
 private:
 	WipeTowerPrusaMM();
@@ -331,6 +335,9 @@ private:
 	std::vector<WipeTowerInfo> m_plan; 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
 	std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
 
+    // Stores information about used filament length per extruder:
+    std::vector<float> m_used_filament_length;
+
 
 	// Returns gcode for wipe tower brim
 	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index bd14837d9..ba8abd040 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1193,6 +1193,8 @@ void Print::_make_wipe_tower()
     }
     m_wipe_tower_final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
 		wipe_tower.tool_change((unsigned int)-1, false));
+
+    m_wipe_tower_used_filament = wipe_tower.get_used_filament();
 }
 
 std::string Print::output_filename()
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index e3430ad0e..537070a34 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -240,7 +240,7 @@ public:
     // TODO: status_cb
     std::string                     estimated_normal_print_time;
     std::string                     estimated_silent_print_time;
-    double                          total_used_filament, total_extruded_volume, total_cost, total_weight;
+    double                          total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament;
     std::map<size_t, float>         filament_stats;
     PrintState<PrintStep, psCount>  state;
 
@@ -309,6 +309,7 @@ public:
     std::unique_ptr<WipeTower::ToolChangeResult>          m_wipe_tower_priming;
     std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
     std::unique_ptr<WipeTower::ToolChangeResult>          m_wipe_tower_final_purge;
+    std::vector<float>                                    m_wipe_tower_used_filament;
 
     std::string output_filename();
     std::string output_filepath(const std::string &path);
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index 717064916..50f899f2c 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -276,6 +276,26 @@ Print::total_cost(...)
             THIS->total_cost = (double)SvNV(ST(1));
         }
         RETVAL = THIS->total_cost;
+    OUTPUT:
+        RETVAL
+
+double
+Print::total_wipe_tower_cost(...)
+    CODE:
+        if (items > 1) {
+            THIS->total_wipe_tower_cost = (double)SvNV(ST(1));
+        }
+        RETVAL = THIS->total_wipe_tower_cost;
+    OUTPUT:
+        RETVAL
+
+double
+Print::total_wipe_tower_filament(...)
+    CODE:
+        if (items > 1) {
+            THIS->total_wipe_tower_filament = (double)SvNV(ST(1));
+        }
+        RETVAL = THIS->total_wipe_tower_filament;
     OUTPUT:
         RETVAL        
 %}

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

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

diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 26a6fdec3..77efbb29b 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -238,7 +238,7 @@ sub _update {
             my @expolygons = ();
             foreach my $volume (@{$self->{model_object}->volumes}) {
                 next if !$volume->mesh;
-                next if $volume->modifier;
+                next if !$volume->model_part;
                 my $expp = $volume->mesh->slice([ $z_cut ])->[0];
                 push @expolygons, @$expp;
             }
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 783c1a9f5..7e9ee05dd 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -16,6 +16,8 @@ use base 'Wx::Panel';
 use constant ICON_OBJECT        => 0;
 use constant ICON_SOLIDMESH     => 1;
 use constant ICON_MODIFIERMESH  => 2;
+use constant ICON_SUPPORT_ENFORCER => 3;
+use constant ICON_SUPPORT_BLOCKER => 4;
 
 sub new {
     my ($class, $parent, %params) = @_;
@@ -35,7 +37,7 @@ sub new {
         y               => 0,
         z               => 0,
     };
-    
+
     # create TreeCtrl
     my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], 
         wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT
@@ -46,6 +48,8 @@ sub new {
         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG));     # ICON_OBJECT
         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG));   # ICON_SOLIDMESH
         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG));    # ICON_MODIFIERMESH
+        $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_enforcer.png"), wxBITMAP_TYPE_PNG));    # ICON_SUPPORT_ENFORCER
+        $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_blocker.png"), wxBITMAP_TYPE_PNG));    # ICON_SUPPORT_BLOCKER
         
         my $rootId = $tree->AddRoot("Object", ICON_OBJECT);
         $tree->SetPlData($rootId, { type => 'object' });
@@ -89,7 +93,14 @@ sub new {
     $self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
     
     # part settings panel
-    $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
+    $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub {
+        my ($key, $value) = @_;
+        wxTheApp->CallAfter(sub { 
+            $self->set_part_type($value) if ($key eq "part_type");
+            $self->{part_settings_changed} = 1;
+            $self->_update_canvas; 
+        });
+    });
     my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
     $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
 
@@ -225,8 +236,11 @@ sub reload_tree {
     my $selectedId = $rootId;
     foreach my $volume_id (0..$#{$object->volumes}) {
         my $volume = $object->volumes->[$volume_id];
-        
-        my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
+        my $icon = 
+            $volume->modifier ? ICON_MODIFIERMESH : 
+            $volume->support_enforcer ? ICON_SUPPORT_ENFORCER :
+            $volume->support_blocker ? ICON_SUPPORT_BLOCKER :
+            ICON_SOLIDMESH;
         my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon);
         if ($volume_id == $selected_volume_idx) {
             $selectedId = $itemId;
@@ -288,6 +302,8 @@ sub selection_changed {
     
     if (my $itemData = $self->get_selection) {
         my ($config, @opt_keys);
+        my $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_OBJECT;
+        my $support = 0;
         if ($itemData->{type} eq 'volume') {
             # select volume in 3D preview
             if ($self->{canvas}) {
@@ -301,16 +317,24 @@ sub selection_changed {
             # attach volume config to settings panel
             my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
    
-            if ($volume->modifier) {
+            if (! $volume->model_part) {
                 $self->{optgroup_movers}->enable;
+                if ($volume->support_enforcer || $volume->support_blocker) {
+                    $support = 1;
+                    $type = $volume->support_enforcer ? 
+                        Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER :
+                        Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER;
+                } else {
+                    $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER;
+                }
             } else {
+                $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART;
                 $self->{optgroup_movers}->disable;
             }
             $config = $volume->config;
             $self->{staticbox}->SetLabel('Part Settings');
-            
             # get default values
-            @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys};
+            @opt_keys = $support ? () : @{Slic3r::Config::PrintRegion->new->get_keys};
         } elsif ($itemData->{type} eq 'object') {
             # select nothing in 3D preview
             
@@ -323,33 +347,54 @@ sub selection_changed {
         # get default values
         my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
 
-       # decide which settings will be shown by default
+        # decide which settings will be shown by default
         if ($itemData->{type} eq 'object') {
             $config->set_ifndef('wipe_into_objects', 0);
             $config->set_ifndef('wipe_into_infill', 0);
         }
 
         # append default extruder
-        push @opt_keys, 'extruder';
-        $default_config->set('extruder', 0);
-        $config->set_ifndef('extruder', 0);
+        if (! $support) {
+            push @opt_keys, 'extruder';
+            $default_config->set('extruder', 0);
+            $config->set_ifndef('extruder', 0);
+        }
+        $self->{settings_panel}->set_type($type);
         $self->{settings_panel}->set_default_config($default_config);
         $self->{settings_panel}->set_config($config);
         $self->{settings_panel}->set_opt_keys(\@opt_keys);
 
         # disable minus icon to remove the settings
-        if ($itemData->{type} eq 'object') {
-            $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
-	} else {
-            $self->{settings_panel}->set_fixed_options([qw(extruder)]);
-        }
-
+        my $fixed_options = 
+            ($itemData->{type} eq 'object') ? [qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)] :
+            $support ? [] : [qw(extruder)];
+        $self->{settings_panel}->set_fixed_options($fixed_options);
         $self->{settings_panel}->enable;
     }
     
     Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
 }
 
+sub set_part_type
+{
+    my ($self, $part_type) = @_;
+    if (my $itemData = $self->get_selection) {
+        if ($itemData->{type} eq 'volume') {
+            my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
+            if ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER ||
+                $part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART) {            
+                $volume->set_modifier($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER);
+            } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER) {
+                $volume->set_support_enforcer;
+            } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER) {
+                $volume->set_support_blocker;
+            }
+            # We want the icon of the selected item to be changed as well.
+            $self->reload_tree($itemData->{volume_id});
+        }
+    }
+}
+
 sub on_btn_load {
     my ($self, $is_modifier) = @_;
     
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index 3b10ed99f..a64bff393 100644
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -7,15 +7,20 @@ use warnings;
 use utf8;
 
 use List::Util qw(first);
-use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG
-    wxTheApp);
-use Wx::Event qw(EVT_BUTTON EVT_LEFT_DOWN EVT_MENU);
+use Wx qw(:misc :sizer :button :combobox wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxTheApp);
+use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_LEFT_DOWN EVT_MENU);
 use base 'Wx::ScrolledWindow';
 
 use constant ICON_MATERIAL      => 0;
 use constant ICON_SOLIDMESH     => 1;
 use constant ICON_MODIFIERMESH  => 2;
 
+use constant TYPE_OBJECT        => -1;
+use constant TYPE_PART          => 0;
+use constant TYPE_MODIFIER      => 1;
+use constant TYPE_SUPPORT_ENFORCER => 2;
+use constant TYPE_SUPPORT_BLOCKER => 3;
+
 my %icons = (
     'Advanced'              => 'wand.png',
     'Extruders'             => 'funnel.png',
@@ -36,13 +41,14 @@ sub new {
     $self->{config} = Slic3r::Config->new;
     # On change callback.
     $self->{on_change} = $params{on_change};
+    $self->{type} = TYPE_OBJECT;
     $self->{fixed_options} = {};
     
     $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
     
     $self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
     $self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0);
-    
+
     # option selector
     {
         # create the button
@@ -110,6 +116,16 @@ sub set_opt_keys {
     $self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
 }
 
+sub set_type {
+    my ($self, $type) = @_;
+    $self->{type} = $type;
+    if ($type == TYPE_SUPPORT_ENFORCER || $type == TYPE_SUPPORT_BLOCKER) {
+        $self->{btn_add}->Hide;
+    } else {
+        $self->{btn_add}->Show;
+    }
+}
+
 sub set_fixed_options {
     my ($self, $opt_keys) = @_;
     $self->{fixed_options} = { map {$_ => 1} @$opt_keys };
@@ -121,12 +137,28 @@ sub update_optgroup {
     
     $self->{options_sizer}->Clear(1);
     return if !defined $self->{config};
-    
+
+    if ($self->{type} != TYPE_OBJECT) {
+        my $label = Wx::StaticText->new($self, -1, "Type:"),
+        my $selection = [ "Part", "Modifier", "Support Enforcer", "Support Blocker" ];
+        my $field = Wx::ComboBox->new($self, -1, $selection->[$self->{type}], wxDefaultPosition, Wx::Size->new(160, -1), $selection, wxCB_READONLY);
+        my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
+        $sizer->Add($label, 1, wxEXPAND | wxALL, 5);
+        $sizer->Add($field, 0, wxALL, 5);
+        EVT_COMBOBOX($self, $field, sub {
+            my $idx = $field->GetSelection;  # get index of selected value
+            $self->{on_change}->("part_type", $idx) if $self->{on_change};
+        });
+        $self->{options_sizer}->Add($sizer, 0, wxEXPAND | wxBOTTOM, 0);
+    }
+
     my %categories = ();
-    foreach my $opt_key (@{$self->{config}->get_keys}) {
-        my $category = $Slic3r::Config::Options->{$opt_key}{category};
-        $categories{$category} ||= [];
-        push @{$categories{$category}}, $opt_key;
+    if ($self->{type} != TYPE_SUPPORT_ENFORCER && $self->{type} != TYPE_SUPPORT_BLOCKER) {
+        foreach my $opt_key (@{$self->{config}->get_keys}) {
+            my $category = $Slic3r::Config::Options->{$opt_key}{category};
+            $categories{$category} ||= [];
+            push @{$categories{$category}}, $opt_key;
+        }
     }
     foreach my $category (sort keys %categories) {
         my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
diff --git a/resources/icons/support_blocker.png b/resources/icons/support_blocker.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a66c73f4f6aff7721a84c2ce768d78cf0aac128
GIT binary patch
literal 656
zcmV;B0&o3^P)<h;3K|Lk000e1NJLTq000mG000mO0ssI2kdbIM00006bW%=J00000
z003^L2ax~(010qNS#tmY4#WTe4#WYKD-Ig~00J;cL_t(2k&RMaNRwd{e!hL*T(veE
zv$B}P+}aw;iwbqi1%qykmQYa?SrCyDqrxr<k-|W!n^8p6pi3np3WP8yNVmdTZCF&8
zZ2plZZPUh@e(dLczqgCe+`t>3tMfkRIh^O5_aHwQOgY^tr#t)fIa>v|l+!Iu&j8?5
zD&5$3|LEPgI4e<R(pb*yIFA)js7{@bBbLi$e7>X@x30IEwdTq6#7Z>)zN`NQ%Vfy#
z7XVTy!ZBoaS1;y8HLt0ybQA(_#j70v1iMWx;22}K!q*zRhPDmIWh#2wInrDgO~xe3
zE?yc?r*pE~B})ME0;1i`$y%dKS$aNfe-bh3jGk9Xbvh@zEzSUdQd&?d!ktaiyDDYY
z*7N@0$wxaMN93of$+KL*F~&MVZIn{2vV4nD?>YLAFG!_Rj@k9T;}uy@LTG=>YXyLA
z$DP{^_Yw<<g1~)sZY)*Ln#zYd5A3O_-&^A#lq^}2K?vOxnY2abi`{{(<LM*bKGQ&q
zND?6wfB@j~n-)T7LpbB;4V8&xS<L_dn#6+39~v3|L2YFKxcp%7XPZ|ymo0TN+GzP(
zp|0!wT<rFLv;39l)SBOaO4Y&o4G;FOo2u5v&bbOoMb)=B08px^G6;5C@iYKTxQrKW
zS`R-9_nmz?6aOVK>Ej`Df#>9`*nR_mu-4n!x_N*QLMSDKC>SrNDU|}B-U*!2uup$1
qFjxG()tt0=-#>3KFScK2jQs;LJU(a{EOJ%=0000<MNUMnLSTX)aU}i#

literal 0
HcmV?d00001

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

literal 0
HcmV?d00001

diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 733ff2ad7..804b526c7 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1227,6 +1227,8 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
 
 Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
 {
+	assert(std::abs(2 * offset) < m_resolution);
+
 	typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
 	// 0) Prepare a binary grid.
 	size_t cell_rows = m_rows + 2;
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 945bb1f86..fd911bf4e 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
 
 const char* NAME_KEY = "name";
 const char* MODIFIER_KEY = "modifier";
+const char* VOLUME_TYPE_KEY = "volume_type";
 
 const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
 const char* VALID_OBJECT_TYPES[] =
@@ -1501,7 +1502,9 @@ namespace Slic3r {
                 if (metadata.key == NAME_KEY)
                     volume->name = metadata.value;
                 else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
-                    volume->modifier = true;
+                    volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+                else if (metadata.key == VOLUME_TYPE_KEY)
+                    volume->set_type(ModelVolume::type_from_string(metadata.value));
                 else
                     volume->config.set_deserialize(metadata.key, metadata.value);
             }
@@ -2017,9 +2020,12 @@ namespace Slic3r {
                             if (!volume->name.empty())
                                 stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
 
-                            // stores volume's modifier field
-                            if (volume->modifier)
+                            // stores volume's modifier field (legacy, to support old slicers)
+                            if (volume->is_modifier())
                                 stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+                            // stores volume's type (overrides the modifier field above)
+                            stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " << 
+                                VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
 
                             // stores volume's config data
                             for (const std::string& key : volume->config.keys())
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 886bbae97..5aa922f62 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -458,9 +458,14 @@ void AMFParserContext::endElement(const char * /* name */)
 					p = end + 1;
                 }
                 m_object->layer_height_profile_valid = true;
-            } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
-                // Is this volume a modifier volume?
-                m_volume->modifier = atoi(m_value[1].c_str()) == 1;
+            } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
+                if (strcmp(opt_key, "modifier") == 0) {
+                    // Is this volume a modifier volume?
+                    // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
+                    m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+                } else if (strcmp(opt_key, "volume_type") == 0) {
+                    m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
+                }
             }
         } else if (m_path.size() == 3) {
             if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -781,8 +786,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
                 stream << "        <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
             if (!volume->name.empty())
                 stream << "        <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
-            if (volume->modifier)
+            if (volume->is_modifier())
                 stream << "        <metadata type=\"slic3r.modifier\">1</metadata>\n";
+            stream << "        <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
             for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
                 stream << "        <triangle>\n";
                 for (int j = 0; j < 3; ++j)
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 09b515c2f..e2e84dfd4 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -609,7 +609,7 @@ const BoundingBoxf3& ModelObject::bounding_box() const
     if (! m_bounding_box_valid) {
         BoundingBoxf3 raw_bbox;
         for (const ModelVolume *v : this->volumes)
-            if (! v->modifier)
+            if (v->is_model_part())
                 raw_bbox.merge(v->mesh.bounding_box());
         BoundingBoxf3 bb;
         for (const ModelInstance *i : this->instances)
@@ -640,7 +640,7 @@ TriangleMesh ModelObject::raw_mesh() const
 {
     TriangleMesh mesh;
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
             mesh.merge(v->mesh);
     return mesh;
 }
@@ -651,7 +651,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
 {
     BoundingBoxf3 bb;
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier) {
+        if (v->is_model_part()) {
             if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
             bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
         }
@@ -663,7 +663,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
 {
     BoundingBoxf3 bb;
     for (ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
             bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
     return bb;
 }
@@ -674,7 +674,7 @@ void ModelObject::center_around_origin()
     // center this object around the origin
 	BoundingBoxf3 bb;
 	for (ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
 			bb.merge(v->mesh.bounding_box());
     
     // first align to origin on XYZ
@@ -778,7 +778,7 @@ size_t ModelObject::facets_count() const
 {
     size_t num = 0;
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier)
+        if (v->is_model_part())
             num += v->mesh.stl.stats.number_of_facets;
     return num;
 }
@@ -786,7 +786,7 @@ size_t ModelObject::facets_count() const
 bool ModelObject::needed_repair() const
 {
     for (const ModelVolume *v : this->volumes)
-        if (! v->modifier && v->mesh.needed_repair())
+        if (v->is_model_part() && v->mesh.needed_repair())
             return true;
     return false;
 }
@@ -802,7 +802,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
     lower->input_file = "";
     
     for (ModelVolume *volume : this->volumes) {
-        if (volume->modifier) {
+        if (! volume->is_model_part()) {
             // don't cut modifiers
             upper->add_volume(*volume);
             lower->add_volume(*volume);
@@ -854,7 +854,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
         ModelVolume* new_volume = new_object->add_volume(*mesh);
         new_volume->name        = volume->name;
         new_volume->config      = volume->config;
-        new_volume->modifier    = volume->modifier;
+        new_volume->set_type(volume->type());
         new_volume->material_id(volume->material_id());
         
         new_objects->push_back(new_object);
@@ -868,7 +868,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
 {
     for (const ModelVolume* vol : this->volumes)
     {
-        if (!vol->modifier)
+        if (vol->is_model_part())
         {
             for (ModelInstance* inst : this->instances)
             {
@@ -972,6 +972,37 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
     return m_convex_hull;
 }
 
+ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
+{
+    // Legacy support
+    if (s == "0")
+        return MODEL_PART;
+    if (s == "1")
+        return PARAMETER_MODIFIER;
+    // New type (supporting the support enforcers & blockers)
+    if (s == "ModelPart")
+        return MODEL_PART;
+    if (s == "ParameterModifier")
+        return PARAMETER_MODIFIER;
+    if (s == "SupportEnforcer")
+        return SUPPORT_ENFORCER;
+    if (s == "SupportBlocker")
+        return SUPPORT_BLOCKER;
+}
+
+std::string ModelVolume::type_to_string(const Type t)
+{
+    switch (t) {
+    case MODEL_PART:         return "ModelPart";
+    case PARAMETER_MODIFIER: return "ParameterModifier";
+    case SUPPORT_ENFORCER:   return "SupportEnforcer";
+    case SUPPORT_BLOCKER:    return "SupportBlocker";
+    default:
+        assert(false);
+        return "ModelPart";
+    }
+}
+
 // Split this volume, append the result to the object owning this volume.
 // Return the number of volumes created from this one.
 // This is useful to assign different materials to different volumes of an object.
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index dadd515de..34a7c7cc1 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -165,15 +165,27 @@ public:
     // Configuration parameters specific to an object model geometry or a modifier volume, 
     // overriding the global Slic3r settings and the ModelObject settings.
     DynamicPrintConfig config;
-    // Is it an object to be printed, or a modifier volume?
-    bool modifier;
-    
+
+    enum Type {
+        MODEL_TYPE_INVALID = -1,
+        MODEL_PART = 0,
+        PARAMETER_MODIFIER,
+        SUPPORT_ENFORCER,
+        SUPPORT_BLOCKER,
+    };
+
     // A parent object owning this modifier volume.
-    ModelObject* get_object() const { return this->object; };
+    ModelObject*        get_object() const { return this->object; };
+    Type                type() const { return m_type; }
+    void                set_type(const Type t) { m_type = t; }
+    bool                is_model_part()         const { return m_type == MODEL_PART; }
+    bool                is_modifier()           const { return m_type == PARAMETER_MODIFIER; }
+    bool                is_support_enforcer()   const { return m_type == SUPPORT_ENFORCER; }
+    bool                is_support_blocker()    const { return m_type == SUPPORT_BLOCKER; }
     t_model_material_id material_id() const { return this->_material_id; }
-    void material_id(t_model_material_id material_id);
-    ModelMaterial* material() const;
-    void set_material(t_model_material_id material_id, const ModelMaterial &material);
+    void                material_id(t_model_material_id material_id);
+    ModelMaterial*      material() const;
+    void                set_material(t_model_material_id material_id, const ModelMaterial &material);
     // Split this volume, append the result to the object owning this volume.
     // Return the number of volumes created from this one.
     // This is useful to assign different materials to different volumes of an object.
@@ -184,24 +196,30 @@ public:
     void calculate_convex_hull();
     const TriangleMesh& get_convex_hull() const;
 
+    // Helpers for loading / storing into AMF / 3MF files.
+    static Type         type_from_string(const std::string &s);
+    static std::string  type_to_string(const Type t);
+
 private:
     // Parent object owning this ModelVolume.
-    ModelObject* object;
-    t_model_material_id _material_id;
+    ModelObject*            object;
+    // Is it an object to be printed, or a modifier volume?
+    Type                    m_type;
+    t_model_material_id     _material_id;
     
-    ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
+    ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
     {
         if (mesh.stl.stats.number_of_facets > 1)
             calculate_convex_hull();
     }
-    ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
+    ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
     ModelVolume(ModelObject *object, const ModelVolume &other) :
-        name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
+        name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
     {
         this->material_id(other.material_id());
     }
     ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
-        name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
+        name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
     {
         this->material_id(other.material_id());
         if (mesh.stl.stats.number_of_facets > 1)
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index bd14837d9..830c857c9 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -362,9 +362,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
     // Invalidate all print steps.
     this->invalidate_all_steps();
 
-    for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
+    size_t volume_id = 0;
+    for (const ModelVolume *volume : model_object->volumes) {
+        if (! volume->is_model_part() && ! volume->is_modifier())
+            continue;
         // Get the config applied to this volume.
-        PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
+        PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
         // Find an existing print region with the same config.
         size_t region_id = size_t(-1);
         for (size_t i = 0; i < this->regions.size(); ++ i)
@@ -379,6 +382,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
         }
         // Assign volume to a region.
         object->add_region_volume(region_id, volume_id);
+        ++ volume_id;
     }
 
     // Apply config to print object.
@@ -853,7 +857,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
     for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
         ModelVolume *volume = model_object->volumes[volume_id];
         //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
-        if (! volume->material_id().empty() && ! volume->config.has("extruder"))
+        if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
             volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
     }
 }
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index b16341732..a0cb83e09 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -214,6 +214,10 @@ public:
 
     bool is_printable() const { return !this->_shifted_copies.empty(); }
 
+    // Helpers to slice support enforcer / blocker meshes by the support generator.
+    std::vector<ExPolygons>     slice_support_enforcers() const;
+    std::vector<ExPolygons>     slice_support_blockers() const;
+
 private:
     Print* _print;
     ModelObject* _model_object;
@@ -225,6 +229,7 @@ private:
     ~PrintObject() {}
 
     std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
+    std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
 };
 
 typedef std::vector<PrintObject*> PrintObjectPtrs;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index f9f0b2056..1a8b19c3f 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1734,7 +1734,7 @@ PrintConfigDef::PrintConfigDef()
                    "for the first object layer.");
     def->sidetext = L("mm");
     def->cli = "support-material-contact-distance=f";
-    def->min = 0;
+//    def->min = 0;
     def->enum_values.push_back("0");
     def->enum_values.push_back("0.2");
 	def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 7150ead59..6dd764ae1 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -1320,29 +1320,62 @@ end:
 
 std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
 {
-    std::vector<ExPolygons> layers;
+    std::vector<const ModelVolume*> volumes;
     if (region_id < this->region_volumes.size()) {
-        std::vector<int> &volumes = this->region_volumes[region_id];
-        if (! volumes.empty()) {
-            // Compose mesh.
-            //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
-            TriangleMesh mesh;
-            for (int volume_id : volumes) {
-                ModelVolume *volume = this->model_object()->volumes[volume_id];
-                if (volume->modifier == modifier)
-                    mesh.merge(volume->mesh);
-            }
-            if (mesh.stl.stats.number_of_facets > 0) {
-                // transform mesh
-                // we ignore the per-instance transformations currently and only 
-                // consider the first one
-                this->model_object()->instances.front()->transform_mesh(&mesh, true);
-                // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
-                mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
-                // perform actual slicing
-                TriangleMeshSlicer mslicer(&mesh);
-                mslicer.slice(z, &layers);
-            }
+        for (int volume_id : this->region_volumes[region_id]) {
+            const ModelVolume *volume = this->model_object()->volumes[volume_id];
+            if (modifier ? volume->is_modifier() : volume->is_model_part())
+                volumes.emplace_back(volume);
+        }
+    }
+    return this->_slice_volumes(z, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+{
+    std::vector<const ModelVolume*> volumes;
+    for (const ModelVolume *volume : this->model_object()->volumes)
+        if (volume->is_support_enforcer())
+            volumes.emplace_back(volume);
+    std::vector<float> zs;
+    zs.reserve(this->layers.size());
+    for (const Layer *l : this->layers)
+        zs.emplace_back(l->slice_z);
+    return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+{
+    std::vector<const ModelVolume*> volumes;
+    for (const ModelVolume *volume : this->model_object()->volumes)
+        if (volume->is_support_blocker())
+            volumes.emplace_back(volume);
+    std::vector<float> zs;
+    zs.reserve(this->layers.size());
+    for (const Layer *l : this->layers)
+        zs.emplace_back(l->slice_z);
+    return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
+{
+    std::vector<ExPolygons> layers;
+    if (! volumes.empty()) {
+        // Compose mesh.
+        //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
+        TriangleMesh mesh;
+        for (const ModelVolume *v : volumes)
+            mesh.merge(v->mesh);
+        if (mesh.stl.stats.number_of_facets > 0) {
+            // transform mesh
+            // we ignore the per-instance transformations currently and only 
+            // consider the first one
+            this->model_object()->instances.front()->transform_mesh(&mesh, true);
+            // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
+            mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
+            // perform actual slicing
+            TriangleMeshSlicer mslicer(&mesh);
+            mslicer.slice(z, &layers);
         }
     }
     return layers;
diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp
index 71fcc876a..5bb1fffb3 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -59,7 +59,7 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
 
 coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
 {
-    return this->nozzle_dmr_avg(print_config) * this->config.bridge_flow_ratio.value;
+    return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value);
 }
 
 }
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
index e9295d1e3..d3fbcc7cb 100644
--- a/xs/src/libslic3r/Slicing.cpp
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
     // 1) Initialize the SlicingAdaptive class with the object meshes.
     SlicingAdaptive as;
     as.set_slicing_parameters(slicing_params);
-    for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
-        if (! (*it)->modifier)
-            as.add_mesh(&(*it)->mesh);
+    for (const ModelVolume *volume : volumes)
+        if (volume->is_model_part())
+            as.add_mesh(&volume->mesh);
     as.prepare();
 
     // 2) Generate layers using the algorithm of @platsch 
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 1bf9675df..e1a8995a6 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -283,7 +283,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
         object, bottom_contacts, top_contacts, layer_storage);
 
 //    this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
-    this->trim_support_layers_by_object(object, top_contacts, 0., 0., m_gap_xy);
+    this->trim_support_layers_by_object(object, top_contacts, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
 
 #ifdef SLIC3R_DEBUG
     for (const MyLayer *layer : top_contacts)
@@ -428,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
 {
     // 1) Count the new polygons first.
     size_t n_polygons_new = 0;
-    for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
-        const LayerRegion       &region = *(*it_region);
-        const SurfaceCollection &slices = region.slices;
-        for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
-            const Surface &surface = *it;
+    for (const LayerRegion *region : layer.regions)
+        for (const Surface &surface : region->slices.surfaces)
             if (surface.surface_type == surface_type)
                 n_polygons_new += surface.expolygon.holes.size() + 1;
-        }
-    }
-
     // 2) Collect the new polygons.
     Polygons out;
     out.reserve(n_polygons_new);
-    for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
-        const LayerRegion       &region = *(*it_region);
-        const SurfaceCollection &slices = region.slices;
-        for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
-            const Surface &surface = *it;
+    for (const LayerRegion *region : layer.regions)
+        for (const Surface &surface : region->slices.surfaces)
             if (surface.surface_type == surface_type)
                 polygons_append(out, surface.expolygon);
-        }
-    }
-
     return out;
 }
 
@@ -460,8 +450,8 @@ Polygons collect_slices_outer(const Layer &layer)
 {
     Polygons out;
     out.reserve(out.size() + layer.slices.expolygons.size());
-    for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it)
-        out.push_back(it->contour);
+    for (const ExPolygon &expoly : layer.slices.expolygons)
+        out.emplace_back(expoly.contour);
     return out;
 }
 
@@ -469,8 +459,11 @@ class SupportGridPattern
 {
 public:
     SupportGridPattern(
+        // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
         const Polygons &support_polygons, 
-        const Polygons &trimming_polygons, 
+        // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
+        const Polygons &trimming_polygons,
+        // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing()
         coordf_t        support_spacing, 
         coordf_t        support_angle) :
         m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons),
@@ -493,7 +486,8 @@ public:
         m_grid.set_bbox(bbox);
         m_grid.create(*m_support_polygons, grid_resolution);
         m_grid.calculate_sdf();
-        // Extract a bounding contour from the grid, trim by the object.
+        // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+        // polygons if ever these polygons get split into parts by the trimming polygons.
         m_island_samples = island_samples(*m_support_polygons);
     }
 
@@ -504,19 +498,19 @@ public:
     Polygons extract_support(const coord_t offset_in_grid)
     {
         // Generate islands, so each island may be tested for overlap with m_island_samples.
+        assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
         ExPolygons islands = diff_ex(
             m_grid.contours_simplified(offset_in_grid),
             *m_trimming_polygons, false);
 
         // Extract polygons, which contain some of the m_island_samples.
         Polygons out;
-        std::vector<std::pair<Point,bool>> samples_inside;
-
         for (ExPolygon &island : islands) {
             BoundingBox bbox = get_extents(island.contour);
+            // Samples are sorted lexicographically.
             auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), bbox.min - Point(1, 1));
             auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), bbox.max + Point(1, 1));
-            samples_inside.clear();
+            std::vector<std::pair<Point,bool>> samples_inside;
             for (auto it = it_lower; it != it_upper; ++ it)
                 if (bbox.contains(*it))
                     samples_inside.push_back(std::make_pair(*it, false));
@@ -577,8 +571,10 @@ public:
 private:
     SupportGridPattern& operator=(const SupportGridPattern &rhs);
 
+#if 0
     // Get some internal point of an expolygon, to be used as a representative
     // sample to test, whether this island is inside another island.
+    //FIXME this was quick, but not sufficiently robust.
     static Point island_sample(const ExPolygon &expoly)
     {
         // Find the lowest point lexicographically.
@@ -599,7 +595,10 @@ private:
         double coef = 20. / sqrt(l2);
         return Point(p2.x + coef * v.x, p2.y + coef * v.y);
     }
+#endif
 
+    // Sample one internal point per expolygon.
+    // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
     static Points island_samples(const ExPolygons &expolygons)
     {
         Points pts;
@@ -637,6 +636,8 @@ private:
     coordf_t                m_support_spacing;
 
     Slic3r::EdgeGrid::Grid  m_grid;
+    // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
+    // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
     Points                  m_island_samples;
 };
 
@@ -804,6 +805,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     ++ iRun; 
 #endif /* SLIC3R_DEBUG */
 
+    // Slice support enforcers / support blockers.
+    std::vector<ExPolygons> enforcers = object.slice_support_enforcers();
+    std::vector<ExPolygons> blockers  = object.slice_support_blockers();
+
     // Output layers, sorted by top Z.
     MyLayersPtr contact_out;
 
@@ -841,10 +846,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
     // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
     size_t num_layers = this->has_support() ? object.layer_count() : 1;
+    // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, 
+    // and the other for the overhangs extruded with a normal flow.
     contact_out.assign(num_layers * 2, nullptr);
     tbb::spin_mutex layer_storage_mutex;
     tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
-        [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
+        [this, &object, &buildplate_covered, &enforcers, &blockers, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
             for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) 
             {
                 const Layer &layer = *object.layers[layer_id];
@@ -855,6 +862,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                 Polygons contact_polygons;
                 Polygons slices_margin_cached;
                 float    slices_margin_cached_offset = -1.;
+                Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons);
+                // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
+                float    no_interface_offset = 0.f;
                 if (layer_id == 0) {
                     // This is the first object layer, so the object is being printed on a raft and
                     // we're here just to get the object footprint for the raft.
@@ -869,6 +879,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // Extrusion width accounts for the roundings of the extrudates.
                         // It is the maximum widh of the extrudate.
                         float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
+                        no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
                         float lower_layer_offset = 
                             (layer_id < this->m_object_config->support_material_enforce_layers.value) ? 
                                 // Enforce a full possible support, ignore the overhang angle.
@@ -881,7 +892,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // Overhang polygons for this layer and region.
                         Polygons diff_polygons;
                         Polygons layerm_polygons = to_polygons(layerm->slices);
-                        Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons);
                         if (lower_layer_offset == 0.f) {
                             // Support everything.
                             diff_polygons = diff(layerm_polygons, lower_layer_polygons);
@@ -893,26 +903,58 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         } else {
                             // Get the regions needing a suport, collapse very tiny spots.
                             //FIXME cache the lower layer offset if this layer has multiple regions.
+#if 1
                             diff_polygons = offset2(
                                 diff(layerm_polygons,
-                                     offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
-                                -0.1f*fw, +0.1f*fw);
+                                     offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
+                                //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
+                                // no support at all for not so steep overhangs.
+                                - 0.1f * fw, 0.1f * fw);
+#else
+                            diff_polygons = 
+                                diff(layerm_polygons,
+                                     offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+#endif
                             if (! buildplate_covered.empty()) {
                                 // Don't support overhangs above the top surfaces.
                                 // This step is done before the contact surface is calculated by growing the overhang region.
                                 diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
                             }
-                            if (diff_polygons.empty())
-                                continue;
-                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
-                            diff_polygons = diff(
-                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), 
-                                lower_layer_polygons);
+                            if (! diff_polygons.empty()) {
+	                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
+	                            // This is done to increase size of the supporting columns below, as they are calculated by 
+	                            // propagating these contact surfaces downwards.
+	                            diff_polygons = diff(
+	                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), 
+	                                lower_layer_polygons);
+							}
+
+                            if (! enforcers.empty()) {
+                                // Apply the "support enforcers".
+                                //FIXME add the "enforcers" to the sparse support regions only.
+                                const ExPolygons &enforcer = enforcers[layer_id - 1];
+                                if (! enforcer.empty()) {
+                                    // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+                                    Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)),
+                                            offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                                    if (! new_contacts.empty()) {
+                                        if (diff_polygons.empty())
+                                            diff_polygons = std::move(new_contacts);
+                                        else
+                                            diff_polygons = union_(diff_polygons, new_contacts);
+                                    }
+                                }
+                            }
+                        }
+                        // Apply the "support blockers".
+                        if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) {
+                            // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+                            diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id]));
                         }
                         if (diff_polygons.empty())
                             continue;
 
-                        #ifdef SLIC3R_DEBUG                        
+                        #ifdef SLIC3R_DEBUG
                         {
                             ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", 
                                 iRun, layer_id, 
@@ -939,7 +981,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                             union_ex(diff_polygons, false));
                         #endif /* SLIC3R_DEBUG */
 
-                        if (this->has_contact_loops())
+                        //FIXME the overhang_polygons are used to construct the support towers as well.
+                        //if (this->has_contact_loops())
+                            // Store the exact contour of the overhang for the contact loops.
                             polygons_append(overhang_polygons, diff_polygons);
 
                         // Let's define the required contact area by using a max gap of half the upper 
@@ -948,12 +992,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         // on the other side of the object (if it's very thin).
                         {
                             //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
+                            //FIXME one should trim with the layer span colliding with the support layer, this layer
+                            // may be lower than lower_layer, so the support area needed may need to be actually bigger!
+                            // For the same reason, the non-bridging support area may be smaller than the bridging support area!
                             float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy))); 
                             if (slices_margin_cached_offset != slices_margin_offset) {
                                 slices_margin_cached_offset = slices_margin_offset;
                                 slices_margin_cached = (slices_margin_offset == 0.f) ? 
-                                    to_polygons(lower_layer.slices.expolygons) :
-                                    offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                                    lower_layer_polygons :
+                                    offset2(to_polygons(lower_layer.slices.expolygons), - scale_(- no_interface_offset * 0.5f), slices_margin_offset + scale_(- no_interface_offset * 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS);
                                 if (! buildplate_covered.empty()) {
                                     // Trim the inflated contact surfaces by the top surfaces as well.
                                     polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -1028,7 +1075,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                     // Align the layer with the 1st layer height.
                                     bridging_print_z = m_slicing_params.first_print_layer_height;
                                 }
-                                if (bridging_print_z != new_layer.print_z) {
+                                if (bridging_print_z < new_layer.print_z - EPSILON) {
                                     // Allocate the new layer.
                                     bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
                                     bridging_layer->idx_object_layer_above = layer_id;
@@ -1051,20 +1098,38 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         contact_polygons, 
                         // Trimming polygons, to trim the stretched support islands.
                         slices_margin_cached,
-                        // How much to offset the extracted contour outside of the grid.
+                        // Grid resolution.
                         m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
                         Geometry::deg2rad(m_object_config->support_material_angle.value));
-                    // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
-                    new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
-                    // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
+                    // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
                     new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3));
+                    // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
+                    if (layer_id == 0) {
+                    // if (no_interface_offset == 0.f) {
+                        new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
+                    } else  {
+                        //Polygons dense_interface_polygons = diff(overhang_polygons, offset(lower_layer_polygons, scale_(no_interface_offset * 0.7f)));
+                        Polygons dense_interface_polygons = diff(overhang_polygons, 
+                            offset2(lower_layer_polygons, scale_(- no_interface_offset * 0.5f), scale_(no_interface_offset * (0.7f + 0.5f)), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                        if (! dense_interface_polygons.empty()) {
+                            SupportGridPattern support_grid_pattern(
+                                // Support islands, to be stretched into a grid.
+                                dense_interface_polygons, 
+                                // Trimming polygons, to trim the stretched support islands.
+                                slices_margin_cached,
+                                // Grid resolution.
+                                m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
+                                Geometry::deg2rad(m_object_config->support_material_angle.value));                        
+                            new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
+                        }
+                    }
 
                     // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
                     // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
                     
                     // Store the overhang polygons.
                     // The overhang polygons are used in the path generator for planning of the contact loops.
-                    // if (this->has_contact_loops())
+                    // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
                     new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
                     contact_out[layer_id * 2] = &new_layer;
                     if (bridging_layer != nullptr) {
@@ -1085,11 +1150,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
     // the top contact layer is merged into the bottom contact layer.
     {
-        int k = 0;
-        for (int i = 0; i < int(contact_out.size()); ++ k) {
+		int i = 0;
+		int k = 0;
+		{
+			// Find the span of layers, which are to be printed at the first layer height.
+			int j = 0;
+			for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
+			if (j > 0) {
+				// Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
+				MyLayer &dst = *contact_out.front();
+				for (int u = 1; u < j; ++ u) {
+					MyLayer &src = *contact_out[u];
+					// The union_() does not support move semantic yet, but maybe one day it will.
+					dst.polygons = union_(dst.polygons, std::move(src.polygons));
+					*dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+					*dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+					// Source polygon is no more needed, it will not be refrenced. Release its data.
+					src.reset();
+				}
+				// Snap the first layer to the 1st layer height.
+				dst.print_z  = m_slicing_params.first_print_layer_height;
+				dst.height   = m_slicing_params.first_print_layer_height;
+				dst.bottom_z = 0;
+				++ k;
+			}
+			i = j;
+		}
+        for (; i < int(contact_out.size()); ++ k) {
             // Find the span of layers closer than m_support_layer_height_min.
             int j = i + 1;
-            coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min - EPSILON;
+            coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON;
             for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
             if (i + 1 < j) {
                 // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
@@ -1148,7 +1238,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             const Layer &layer = *object.get_layer(layer_id);
             // Collect projections of all contact areas above or at the same level as this top surface.
             for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) {
-                auto *l = top_contacts[contact_idx];
                 Polygons polygons_new;
                 // Contact surfaces are expanded away from the object, trimmed by the object.
                 // Use a slight positive offset to overlap the touching regions.
@@ -1156,7 +1245,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                 // Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
                 polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON));
 #else
-                // Consume the contact_polygons. The contact polygons are already expanded into a grid form.
+                // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
+                // than the grid cells.
                 polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons));
 #endif
                 // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
@@ -1168,9 +1258,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                 continue;
             Polygons projection_raw = union_(projection);
 
-            // Top surfaces of this layer, to be used to stop the surface volume from growing down.
             tbb::task_group task_group;
             if (! m_object_config->support_material_buildplate_only)
+                // Find the bottom contact layers above the top surfaces of this layer.
                 task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
                     Polygons top = collect_region_slices_by_type(layer, stTop);
         #ifdef SLIC3R_DEBUG
@@ -1206,24 +1296,26 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                                 // Place a bridge flow interface layer over the top surface.
                                 //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
                                 // According to Jindrich the bottom surfaces work well.
+                                //FIXME test the bridging flow instead?
                                 m_support_material_interface_flow.nozzle_diameter;
                             layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
                                 layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
                             layer_new.bottom_z = layer.print_z;
                             layer_new.idx_object_layer_below = layer_id;
                             layer_new.bridging = ! m_slicing_params.soluble_interface;
-                            //FIXME how much to inflate the top surface?
+                            //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
+                            //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
                             layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
                             if (! m_slicing_params.soluble_interface) {
                                 // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
                                 // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
                                 for (size_t top_idx = size_t(std::max<int>(0, contact_idx)); 
-                                    top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min; 
+                                    top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON; 
                                     ++ top_idx) {
-                                    if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) {
+                                    if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) {
                                         // A top layer has been found, which is close to the new bottom layer.
                                         coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z;
-                                        assert(std::abs(diff) <= this->m_support_layer_height_min);
+                                        assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON);
                                         if (diff > 0.) {
                                             // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
                                             assert(diff < layer_new.height + EPSILON);
@@ -1247,10 +1339,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                                 union_ex(layer_new.polygons, false));
                 #endif /* SLIC3R_DEBUG */
                             // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
+                            //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
                             touching = offset(touching, float(SCALED_EPSILON));
                             for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
                                 const Layer &layer_above = *object.layers[layer_id_above];
-                                if (layer_above.print_z > layer_new.print_z + EPSILON)
+                                if (layer_above.print_z > layer_new.print_z - EPSILON)
                                     break; 
                                 if (! layer_support_areas[layer_id_above].empty()) {
 #ifdef SLIC3R_DEBUG
@@ -1303,7 +1396,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                     projection, 
                     // Trimming polygons, to trim the stretched support islands.
                     trimming,
-                    // How much to offset the extracted contour outside of the grid.
+                    // Grid spacing.
                     m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
                     Geometry::deg2rad(m_object_config->support_material_angle.value));
                 tbb::task_group task_group_inner;
@@ -1341,7 +1434,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
             task_group.wait();
         }
         std::reverse(bottom_contacts.begin(), bottom_contacts.end());
-        trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
+//        trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
+        trim_support_layers_by_object(object, bottom_contacts, 
+            m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, 
+            m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
     } // ! top_contacts.empty()
 
     return bottom_contacts;
@@ -1658,9 +1755,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
                 assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z);
 
                 // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
-                idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, 
-                    [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
-
                 // New polygons for layer_intermediate.
                 Polygons polygons_new;
 
@@ -1679,12 +1773,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
                 // 3) base.print_z > top.print_z  && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
                 // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
                 // 5) base.print_z <= top.print_z  && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
-                int idx_top_contact_overlapping = idx_top_contact_above;
-                while (idx_top_contact_overlapping >= 0 && 
-                       top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON)
-                    -- idx_top_contact_overlapping; 
+                idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, 
+                    [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
                 // Collect all the top_contact layer intersecting with this layer.
-                for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
+                for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
                     MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping];
                     if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON)
                         break;
@@ -1764,7 +1856,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
     ++ iRun;
 #endif /* SLIC3R_DEBUG */
 
-    trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
+//    trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
+    this->trim_support_layers_by_object(object, intermediate_layers, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, 
+        m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
 }
 
 void PrintObjectSupportMaterial::trim_support_layers_by_object(
@@ -1809,7 +1904,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
                     const Layer &object_layer = *object.layers[i];
                     if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
                         break;
-                    polygons_append(polygons_trimming, (Polygons)object_layer.slices);
+                    polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                 }
                 if (! this->m_slicing_params.soluble_interface) {
                     // Collect all bottom surfaces, which will be extruded with a bridging flow.
@@ -1958,11 +2053,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
                     coordf_t top_z    = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z;
                     coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z;
                     // Move idx_top_contact_first up until above the current print_z.
-                    idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; });
+                    idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); //  - EPSILON
                     // Collect the top contact areas above this intermediate layer, below top_z.
                     Polygons polygons_top_contact_projected;
                     for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) {
                         const MyLayer &top_contact_layer = *top_contacts[idx_top_contact];
+                        //FIXME maybe this adds one interface layer in excess?
                         if (top_contact_layer.bottom_z - EPSILON > top_z)
                             break;
                         polygons_append(polygons_top_contact_projected, top_contact_layer.polygons);
@@ -2517,7 +2613,7 @@ void modulate_extrusion_by_overlapping_layers(
                 (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
         }
     private:
-        ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&);
+        ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
         const std::vector<ExtrusionPathFragment> &m_path_fragments;
     };
     const coord_t search_radius = 7;
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index da11cd82c..dcb3bd5b3 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -12,6 +12,7 @@ class PrintConfig;
 class PrintObjectConfig;
 
 // how much we extend support around the actual contact area
+//FIXME this should be dependent on the nozzle diameter!
 #define SUPPORT_MATERIAL_MARGIN 1.5	
 
 // This class manages raft and supports for a single PrintObject.
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 705347094..51e749a58 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -622,7 +622,7 @@ std::vector<int> GLVolumeCollection::load_object(
         const ModelVolume *model_volume = model_object->volumes[volume_idx];
 
         int extruder_id = -1;
-        if (!model_volume->modifier)
+        if (model_volume->is_model_part())
         {
             extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
             if (extruder_id == 0)
@@ -635,7 +635,16 @@ std::vector<int> GLVolumeCollection::load_object(
             volumes_idx.push_back(int(this->volumes.size()));
             float color[4];
             memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
-            color[3] = model_volume->modifier ? 0.5f : 1.f;
+            if (model_volume->is_support_blocker()) {
+                color[0] = 1.0f;
+                color[1] = 0.2f;
+                color[2] = 0.2f;
+            } else if (model_volume->is_support_enforcer()) {
+                color[0] = 0.2f;
+                color[1] = 0.2f;
+                color[2] = 1.0f;
+            }
+            color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
             this->volumes.emplace_back(new GLVolume(color));
             GLVolume &v = *this->volumes.back();
             if (use_VBOs)
@@ -658,15 +667,15 @@ std::vector<int> GLVolumeCollection::load_object(
             else if (drag_by == "instance")
                 v.drag_group_id = obj_idx * 1000 + instance_idx;
 
-            if (!model_volume->modifier)
+            if (model_volume->is_model_part())
             {
                 v.set_convex_hull(model_volume->get_convex_hull());
                 v.layer_height_texture = layer_height_texture;
                 if (extruder_id != -1)
                     v.extruder_id = extruder_id;
             }
-            v.is_modifier = model_volume->modifier;
-            v.shader_outside_printer_detection_enabled = !model_volume->modifier;
+            v.is_modifier = ! model_volume->is_model_part();
+            v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
             v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
             v.set_angle_z(instance->rotation);
             v.set_scale_factor(instance->scaling_factor);
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 182963257..0f9b5cd15 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -340,9 +340,19 @@ ModelMaterial::attributes()
         %code%{ RETVAL = &THIS->mesh; %};
     
     bool modifier()
-        %code%{ RETVAL = THIS->modifier; %};
+        %code%{ RETVAL = THIS->is_modifier(); %};
     void set_modifier(bool modifier)
-        %code%{ THIS->modifier = modifier; %};
+        %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %};
+    bool model_part()
+        %code%{ RETVAL = THIS->is_model_part(); %};
+    bool support_enforcer()
+        %code%{ RETVAL = THIS->is_support_enforcer(); %};
+    void set_support_enforcer()
+        %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %};
+    bool support_blocker()
+        %code%{ RETVAL = THIS->is_support_blocker(); %};
+    void set_support_blocker()
+        %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
 
     size_t split(unsigned int max_extruders);
     

From 961d8942187dcda70d87b358bceb6cf0a967a6b7 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 7 Sep 2018 08:43:21 +0200
Subject: [PATCH 13/22] Added a Layout call for 'Sliced Info' box to show
 sliders when needed

---
 lib/Slic3r/GUI/Plater.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 7c9e9899e..56c21bbbb 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1687,6 +1687,7 @@ sub print_info_box_show {
 
     $scrolled_window_sizer->Show(2, $show);
     $scrolled_window_panel->Layout;
+    $self->Layout;
 }
 
 sub do_print {

From ec3e1403b645928f0c19ae225b9924848b8aa6bb Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Fri, 7 Sep 2018 10:24:05 +0200
Subject: [PATCH 14/22] Cleaning up and fixing localization issues with
 AppController.

---
 xs/CMakeLists.txt                             |   3 +-
 xs/src/slic3r/AppController.cpp               | 211 +-----------------
 xs/src/slic3r/AppController.hpp               |  52 ++---
 xs/src/slic3r/AppControllerWx.cpp             |  73 +++---
 ...essIndicator.hpp => ProgressIndicator.hpp} |  33 ++-
 xs/src/slic3r/Strings.hpp                     |  10 -
 xs/xsp/AppController.xsp                      |   3 -
 7 files changed, 72 insertions(+), 313 deletions(-)
 rename xs/src/slic3r/{IProgressIndicator.hpp => ProgressIndicator.hpp} (62%)
 delete mode 100644 xs/src/slic3r/Strings.hpp

diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index c4d3492a3..efd52ac13 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -267,11 +267,10 @@ add_library(libslic3r_gui STATIC
     ${LIBDIR}/slic3r/Utils/Time.hpp
     ${LIBDIR}/slic3r/Utils/HexFile.cpp
     ${LIBDIR}/slic3r/Utils/HexFile.hpp
-    ${LIBDIR}/slic3r/IProgressIndicator.hpp
+    ${LIBDIR}/slic3r/ProgressIndicator.hpp
     ${LIBDIR}/slic3r/AppController.hpp
     ${LIBDIR}/slic3r/AppController.cpp
     ${LIBDIR}/slic3r/AppControllerWx.cpp
-    ${LIBDIR}/slic3r/Strings.hpp
 )
 
 add_library(admesh STATIC
diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp
index 9394df363..7bd044b25 100644
--- a/xs/src/slic3r/AppController.cpp
+++ b/xs/src/slic3r/AppController.cpp
@@ -43,15 +43,6 @@ namespace GUI {
 PresetBundle* get_preset_bundle();
 }
 
-static const PrintObjectStep STEP_SLICE                 = posSlice;
-static const PrintObjectStep STEP_PERIMETERS            = posPerimeters;
-static const PrintObjectStep STEP_PREPARE_INFILL        = posPrepareInfill;
-static const PrintObjectStep STEP_INFILL                = posInfill;
-static const PrintObjectStep STEP_SUPPORTMATERIAL       = posSupportMaterial;
-static const PrintStep STEP_SKIRT                       = psSkirt;
-static const PrintStep STEP_BRIM                        = psBrim;
-static const PrintStep STEP_WIPE_TOWER                  = psWipeTower;
-
 AppControllerBoilerplate::ProgresIndicatorPtr
 AppControllerBoilerplate::global_progress_indicator() {
     ProgresIndicatorPtr ret;
@@ -71,193 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator(
     pri_data_->m.unlock();
 }
 
-void PrintController::make_skirt()
-{
-    assert(print_ != nullptr);
-
-    // prerequisites
-    for(auto obj : print_->objects) make_perimeters(obj);
-    for(auto obj : print_->objects) infill(obj);
-    for(auto obj : print_->objects) gen_support_material(obj);
-
-    if(!print_->state.is_done(STEP_SKIRT)) {
-        print_->state.set_started(STEP_SKIRT);
-        print_->skirt.clear();
-        if(print_->has_skirt()) print_->_make_skirt();
-
-        print_->state.set_done(STEP_SKIRT);
-    }
-}
-
-void PrintController::make_brim()
-{
-    assert(print_ != nullptr);
-
-    // prerequisites
-    for(auto obj : print_->objects) make_perimeters(obj);
-    for(auto obj : print_->objects) infill(obj);
-    for(auto obj : print_->objects) gen_support_material(obj);
-    make_skirt();
-
-    if(!print_->state.is_done(STEP_BRIM)) {
-        print_->state.set_started(STEP_BRIM);
-
-        // since this method must be idempotent, we clear brim paths *before*
-        // checking whether we need to generate them
-        print_->brim.clear();
-
-        if(print_->config.brim_width > 0) print_->_make_brim();
-
-        print_->state.set_done(STEP_BRIM);
-    }
-}
-
-void PrintController::make_wipe_tower()
-{
-    assert(print_ != nullptr);
-
-    // prerequisites
-    for(auto obj : print_->objects) make_perimeters(obj);
-    for(auto obj : print_->objects) infill(obj);
-    for(auto obj : print_->objects) gen_support_material(obj);
-    make_skirt();
-    make_brim();
-
-    if(!print_->state.is_done(STEP_WIPE_TOWER)) {
-        print_->state.set_started(STEP_WIPE_TOWER);
-
-        // since this method must be idempotent, we clear brim paths *before*
-        // checking whether we need to generate them
-        print_->brim.clear();
-
-        if(print_->has_wipe_tower()) print_->_make_wipe_tower();
-
-        print_->state.set_done(STEP_WIPE_TOWER);
-    }
-}
-
-void PrintController::slice(PrintObject *pobj)
-{
-    assert(pobj != nullptr && print_ != nullptr);
-
-    if(pobj->state.is_done(STEP_SLICE)) return;
-
-    pobj->state.set_started(STEP_SLICE);
-
-    pobj->_slice();
-
-    auto msg = pobj->_fix_slicing_errors();
-    if(!msg.empty()) report_issue(IssueType::WARN, msg);
-
-    // simplify slices if required
-    if (print_->config.resolution)
-        pobj->_simplify_slices(scale_(print_->config.resolution));
-
-
-    if(pobj->layers.empty())
-        report_issue(IssueType::ERR,
-                     _(L("No layers were detected. You might want to repair your "
-                     "STL file(s) or check their size or thickness and retry"))
-                     );
-
-    pobj->state.set_done(STEP_SLICE);
-}
-
-void PrintController::make_perimeters(PrintObject *pobj)
-{
-    assert(pobj != nullptr);
-
-    slice(pobj);
-
-    if (!pobj->state.is_done(STEP_PERIMETERS)) {
-        pobj->_make_perimeters();
-    }
-}
-
-void PrintController::infill(PrintObject *pobj)
-{
-    assert(pobj != nullptr);
-
-    make_perimeters(pobj);
-
-    if (!pobj->state.is_done(STEP_PREPARE_INFILL)) {
-        pobj->state.set_started(STEP_PREPARE_INFILL);
-
-        pobj->_prepare_infill();
-
-        pobj->state.set_done(STEP_PREPARE_INFILL);
-    }
-
-    pobj->_infill();
-}
-
-void PrintController::gen_support_material(PrintObject *pobj)
-{
-    assert(pobj != nullptr);
-
-    // prerequisites
-    slice(pobj);
-
-    if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) {
-        pobj->state.set_started(STEP_SUPPORTMATERIAL);
-
-        pobj->clear_support_layers();
-
-        if((pobj->config.support_material || pobj->config.raft_layers > 0)
-                && pobj->layers.size() > 1) {
-            pobj->_generate_support_material();
-        }
-
-        pobj->state.set_done(STEP_SUPPORTMATERIAL);
-    }
-}
-
-void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri)
-{
-    auto st = pri->state();
-
-    Slic3r::trace(3, "Starting the slicing process.");
-
-    pri->update(st+20, _(L("Generating perimeters")));
-    for(auto obj : print_->objects) make_perimeters(obj);
-
-    pri->update(st+60, _(L("Infilling layers")));
-    for(auto obj : print_->objects) infill(obj);
-
-    pri->update(st+70, _(L("Generating support material")));
-    for(auto obj : print_->objects) gen_support_material(obj);
-
-    pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")),
-                     print_->total_weight, print_->total_cost);
-    pri->state(st+85);
-
-
-    pri->update(st+88, _(L("Generating skirt")));
-    make_skirt();
-
-
-    pri->update(st+90, _(L("Generating brim")));
-    make_brim();
-
-    pri->update(st+95, _(L("Generating wipe tower")));
-    make_wipe_tower();
-
-    pri->update(st+100, _(L("Done")));
-
-    // time to make some statistics..
-
-    Slic3r::trace(3, _(L("Slicing process finished.")));
-}
-
-void PrintController::slice()
-{
-    auto pri = global_progress_indicator();
-    if(!pri) pri = create_progress_indicator(100, L("Slicing"));
-    slice(pri);
-}
-
-void IProgressIndicator::message_fmt(
-        const string &fmtstr, ...) {
+void ProgressIndicator::message_fmt(
+        const std::string &fmtstr, ...) {
     std::stringstream ss;
     va_list args;
     va_start(args, fmtstr);
@@ -288,7 +94,6 @@ const PrintConfig &PrintController::config() const
     return print_->config;
 }
 
-
 void AppController::arrange_model()
 {
     auto ftr = std::async(
@@ -322,7 +127,7 @@ void AppController::arrange_model()
         for(auto& v : bedpoints)
             bed.append(Point::new_scale(v.x, v.y));
 
-        if(pind) pind->update(0, _(L("Arranging objects...")));
+        if(pind) pind->update(0, L("Arranging objects..."));
 
         try {
             arr::BedShapeHint hint;
@@ -336,20 +141,20 @@ void AppController::arrange_model()
                          false, // create many piles not just one pile
                          [pind, count](unsigned rem) {
                 if(pind)
-                    pind->update(count - rem, _(L("Arranging objects...")));
+                    pind->update(count - rem, L("Arranging objects..."));
             });
         } catch(std::exception& e) {
             std::cerr << e.what() << std::endl;
             report_issue(IssueType::ERR,
-                         _(L("Could not arrange model objects! "
-                         "Some geometries may be invalid.")),
-                         _(L("Exception occurred")));
+                         L("Could not arrange model objects! "
+                         "Some geometries may be invalid."),
+                         L("Exception occurred"));
         }
 
         // Restore previous max value
         if(pind) {
             pind->max(pmax);
-            pind->update(0, _(L("Arranging done.")));
+            pind->update(0, L("Arranging done."));
         }
     });
 
diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp
index e9252b50c..3ef47ffdc 100644
--- a/xs/src/slic3r/AppController.hpp
+++ b/xs/src/slic3r/AppController.hpp
@@ -7,7 +7,7 @@
 #include <atomic>
 #include <iostream>
 
-#include "IProgressIndicator.hpp"
+#include "ProgressIndicator.hpp"
 
 namespace Slic3r {
 
@@ -15,7 +15,8 @@ class Model;
 class Print;
 class PrintObject;
 class PrintConfig;
-
+class ProgressStatusBar;
+class DynamicPrintConfig;
 
 /**
  * @brief A boilerplate class for creating application logic. It should provide
@@ -33,7 +34,7 @@ class AppControllerBoilerplate {
 public:
 
     /// A Progress indicator object smart pointer
-    using ProgresIndicatorPtr = std::shared_ptr<IProgressIndicator>;
+    using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
 
 private:
     class PriData;   // Some structure to store progress indication data
@@ -46,7 +47,7 @@ public:
     AppControllerBoilerplate();
     ~AppControllerBoilerplate();
 
-    using Path = string;
+    using Path = std::string;
     using PathList = std::vector<Path>;
 
     /// Common runtime issue types
@@ -67,20 +68,20 @@ public:
      * @return Returns a list of paths choosed by the user.
      */
     PathList query_destination_paths(
-            const string& title,
+            const std::string& title,
             const std::string& extensions) const;
 
     /**
      * @brief Same as query_destination_paths but works for directories only.
      */
     PathList query_destination_dirs(
-            const string& title) const;
+            const std::string& title) const;
 
     /**
      * @brief Same as query_destination_paths but returns only one path.
      */
     Path query_destination_path(
-            const string& title,
+            const std::string& title,
             const std::string& extensions,
             const std::string& hint = "") const;
 
@@ -95,11 +96,11 @@ public:
      * title.
      */
     bool report_issue(IssueType issuetype,
-                      const string& description,
-                      const string& brief);
+                      const std::string& description,
+                      const std::string& brief);
 
     bool report_issue(IssueType issuetype,
-                      const string& description);
+                      const std::string& description);
 
     /**
      * @brief Return the global progress indicator for the current controller.
@@ -150,12 +151,12 @@ protected:
      */
     ProgresIndicatorPtr create_progress_indicator(
             unsigned statenum,
-            const string& title,
-            const string& firstmsg) const;
+            const std::string& title,
+            const std::string& firstmsg) const;
 
     ProgresIndicatorPtr create_progress_indicator(
             unsigned statenum,
-            const string& title) const;
+            const std::string& title) const;
 
     // This is a global progress indicator placeholder. In the Slic3r UI it can
     // contain the progress indicator on the statusbar.
@@ -167,24 +168,6 @@ protected:
  */
 class PrintController: public AppControllerBoilerplate {
     Print *print_ = nullptr;
-protected:
-
-    void make_skirt();
-    void make_brim();
-    void make_wipe_tower();
-
-    void make_perimeters(PrintObject *pobj);
-    void infill(PrintObject *pobj);
-    void gen_support_material(PrintObject *pobj);
-
-    /**
-     * @brief Slice one pront object.
-     * @param pobj The print object.
-     */
-    void slice(PrintObject *pobj);
-
-    void slice(ProgresIndicatorPtr pri);
-
 public:
 
     // Must be public for perl to use it
@@ -199,11 +182,6 @@ public:
         return PrintController::Ptr( new PrintController(print) );
     }
 
-    /**
-     * @brief Slice the loaded print scene.
-     */
-    void slice();
-
     const PrintConfig& config() const;
 };
 
@@ -248,7 +226,7 @@ public:
      * In perl we have a progress indicating status bar on the bottom of the
      * window which is defined and created in perl. We can pass the ID-s of the
      * gauge and the statusbar id and make a wrapper implementation of the
-     * IProgressIndicator interface so we can use this GUI widget from C++.
+     * ProgressIndicator interface so we can use this GUI widget from C++.
      *
      * This function should be called from perl.
      *
diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp
index 4e116c7b9..36a465919 100644
--- a/xs/src/slic3r/AppControllerWx.cpp
+++ b/xs/src/slic3r/AppControllerWx.cpp
@@ -32,11 +32,11 @@ void AppControllerBoilerplate::process_events()
 
 AppControllerBoilerplate::PathList
 AppControllerBoilerplate::query_destination_paths(
-        const string &title,
+        const std::string &title,
         const std::string &extensions) const
 {
 
-    wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+    wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
     dlg.SetWildcard(extensions);
 
     dlg.ShowModal();
@@ -52,11 +52,11 @@ AppControllerBoilerplate::query_destination_paths(
 
 AppControllerBoilerplate::Path
 AppControllerBoilerplate::query_destination_path(
-        const string &title,
+        const std::string &title,
         const std::string &extensions,
         const std::string& hint) const
 {
-    wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+    wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
     dlg.SetWildcard(extensions);
 
     dlg.SetFilename(hint);
@@ -71,8 +71,8 @@ AppControllerBoilerplate::query_destination_path(
 }
 
 bool AppControllerBoilerplate::report_issue(IssueType issuetype,
-                                 const string &description,
-                                 const string &brief)
+                                 const std::string &description,
+                                 const std::string &brief)
 {
     auto icon = wxICON_INFORMATION;
     auto style = wxOK|wxCENTRE;
@@ -84,15 +84,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype,
     case IssueType::FATAL:  icon = wxICON_ERROR;
     }
 
-    auto ret = wxMessageBox(description, brief, icon | style);
+    auto ret = wxMessageBox(_(description), _(brief), icon | style);
     return ret != wxCANCEL;
 }
 
 bool AppControllerBoilerplate::report_issue(
         AppControllerBoilerplate::IssueType issuetype,
-        const string &description)
+        const std::string &description)
 {
-    return report_issue(issuetype, description, string());
+    return report_issue(issuetype, description, std::string());
 }
 
 wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
@@ -104,10 +104,10 @@ namespace  {
  * the main thread as well.
  */
 class GuiProgressIndicator:
-        public IProgressIndicator, public wxEvtHandler {
+        public ProgressIndicator, public wxEvtHandler {
 
     wxProgressDialog gauge_;
-    using Base = IProgressIndicator;
+    using Base = ProgressIndicator;
     wxString message_;
     int range_; wxString title_;
     bool is_asynch_ = false;
@@ -136,8 +136,8 @@ public:
     /// Get the mode of parallel operation.
     inline bool asynch() const { return is_asynch_; }
 
-    inline GuiProgressIndicator(int range, const string& title,
-                                const string& firstmsg) :
+    inline GuiProgressIndicator(int range, const wxString& title,
+                                const wxString& firstmsg) :
         gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
                wxPD_APP_MODAL | wxPD_AUTO_HIDE),
         message_(firstmsg),
@@ -151,11 +151,6 @@ public:
              this, id_);
     }
 
-    virtual void cancel() override {
-        update(max(), "Abort");
-        IProgressIndicator::cancel();
-    }
-
     virtual void state(float val) override {
         state(static_cast<unsigned>(val));
     }
@@ -170,26 +165,28 @@ public:
         } else _state(st);
     }
 
-    virtual void message(const string & msg) override {
-        message_ = msg;
+    virtual void message(const std::string & msg) override {
+        message_ = _(msg);
     }
 
-    virtual void messageFmt(const string& fmt, ...) {
+    virtual void messageFmt(const std::string& fmt, ...) {
         va_list arglist;
         va_start(arglist, fmt);
-        message_ = wxString::Format(wxString(fmt), arglist);
+        message_ = wxString::Format(_(fmt), arglist);
         va_end(arglist);
     }
 
-    virtual void title(const string & title) override {
-        title_ = title;
+    virtual void title(const std::string & title) override {
+        title_ = _(title);
     }
 };
 }
 
 AppControllerBoilerplate::ProgresIndicatorPtr
 AppControllerBoilerplate::create_progress_indicator(
-        unsigned statenum, const string& title, const string& firstmsg) const
+        unsigned statenum,
+        const std::string& title,
+        const std::string& firstmsg) const
 {
     auto pri =
             std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
@@ -202,20 +199,20 @@ AppControllerBoilerplate::create_progress_indicator(
 }
 
 AppControllerBoilerplate::ProgresIndicatorPtr
-AppControllerBoilerplate::create_progress_indicator(unsigned statenum,
-                                                    const string &title) const
+AppControllerBoilerplate::create_progress_indicator(
+        unsigned statenum, const std::string &title) const
 {
-    return create_progress_indicator(statenum, title, string());
+    return create_progress_indicator(statenum, title, std::string());
 }
 
 namespace {
 
 // A wrapper progress indicator class around the statusbar created in perl.
-class Wrapper: public IProgressIndicator, public wxEvtHandler {
+class Wrapper: public ProgressIndicator, public wxEvtHandler {
     wxGauge *gauge_;
     wxStatusBar *stbar_;
-    using Base = IProgressIndicator;
-    std::string message_;
+    using Base = ProgressIndicator;
+    wxString message_;
     AppControllerBoilerplate& ctl_;
 
     void showProgress(bool show = true) {
@@ -223,7 +220,7 @@ class Wrapper: public IProgressIndicator, public wxEvtHandler {
     }
 
     void _state(unsigned st) {
-        if( st <= IProgressIndicator::max() ) {
+        if( st <= ProgressIndicator::max() ) {
             Base::state(st);
 
             if(!gauge_->IsShown()) showProgress(true);
@@ -266,7 +263,7 @@ public:
     virtual void max(float val) override {
         if(val > 1.0) {
             gauge_->SetRange(static_cast<int>(val));
-            IProgressIndicator::max(val);
+            ProgressIndicator::max(val);
         }
     }
 
@@ -280,18 +277,18 @@ public:
         }
     }
 
-    virtual void message(const string & msg) override {
-        message_ = msg;
+    virtual void message(const std::string & msg) override {
+        message_ = _(msg);
     }
 
-    virtual void message_fmt(const string& fmt, ...) override {
+    virtual void message_fmt(const std::string& fmt, ...) override {
         va_list arglist;
         va_start(arglist, fmt);
-        message_ = wxString::Format(fmt, arglist);
+        message_ = wxString::Format(_(fmt), arglist);
         va_end(arglist);
     }
 
-    virtual void title(const string & /*title*/) override {}
+    virtual void title(const std::string & /*title*/) override {}
 
 };
 }
diff --git a/xs/src/slic3r/IProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp
similarity index 62%
rename from xs/src/slic3r/IProgressIndicator.hpp
rename to xs/src/slic3r/ProgressIndicator.hpp
index 704931574..1b064077e 100644
--- a/xs/src/slic3r/IProgressIndicator.hpp
+++ b/xs/src/slic3r/ProgressIndicator.hpp
@@ -3,16 +3,15 @@
 
 #include <string>
 #include <functional>
-#include "Strings.hpp"
 
 namespace Slic3r {
 
 /**
  * @brief Generic progress indication interface.
  */
-class IProgressIndicator {
+class ProgressIndicator {
 public:
-    using CancelFn = std::function<void(void)>; // Cancel functio signature.
+    using CancelFn = std::function<void(void)>; // Cancel function signature.
 
 private:
     float state_ = .0f, max_ = 1.f, step_;
@@ -20,7 +19,7 @@ private:
 
 public:
 
-    inline virtual ~IProgressIndicator() {}
+    inline virtual ~ProgressIndicator() {}
 
     /// Get the maximum of the progress range.
     float max() const { return max_; }
@@ -28,14 +27,14 @@ public:
     /// Get the current progress state
     float state() const { return state_; }
 
-    /// Set the maximum of hte progress range
+    /// Set the maximum of the progress range
     virtual void max(float maxval) { max_ = maxval; }
 
     /// Set the current state of the progress.
     virtual void state(float val)  { state_ = val; }
 
     /**
-     * @brief Number of states int the progress. Can be used insted of giving a
+     * @brief Number of states int the progress. Can be used instead of giving a
      * maximum value.
      */
     virtual void states(unsigned statenum) {
@@ -43,25 +42,19 @@ public:
     }
 
     /// Message shown on the next status update.
-    virtual void message(const string&) = 0;
+    virtual void message(const std::string&) = 0;
 
-    /// Title of the operaton.
-    virtual void title(const string&) = 0;
+    /// Title of the operation.
+    virtual void title(const std::string&) = 0;
 
-    /// Formatted message for the next status update. Works just like sprinf.
-    virtual void message_fmt(const string& fmt, ...);
+    /// Formatted message for the next status update. Works just like sprintf.
+    virtual void message_fmt(const std::string& fmt, ...);
 
     /// Set up a cancel callback for the operation if feasible.
-    inline void on_cancel(CancelFn func) { cancelfunc_ = func; }
+    virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
 
-    /**
-     * Explicitly shut down the progress indicator and call the associated
-     * callback.
-     */
-    virtual void cancel() { cancelfunc_(); }
-
-    /// Convinience function to call message and status update in one function.
-    void update(float st, const string& msg) {
+    /// Convenience function to call message and status update in one function.
+    void update(float st, const std::string& msg) {
         message(msg); state(st);
     }
 };
diff --git a/xs/src/slic3r/Strings.hpp b/xs/src/slic3r/Strings.hpp
deleted file mode 100644
index b267fe064..000000000
--- a/xs/src/slic3r/Strings.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef STRINGS_HPP
-#define STRINGS_HPP
-
-#include "GUI/GUI.hpp"
-
-namespace Slic3r {
-using string = wxString;
-}
-
-#endif // STRINGS_HPP
diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp
index 1b653081d..a578fe0b1 100644
--- a/xs/xsp/AppController.xsp
+++ b/xs/xsp/AppController.xsp
@@ -8,10 +8,7 @@
 %}
 
 %name{Slic3r::PrintController} class PrintController {
-
     PrintController(Print *print);
-
-    void slice();
 };
 
 %name{Slic3r::AppController} class AppController {

From 1acee8900695b7060f2ee136b4da8e3778b407cb Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Fri, 7 Sep 2018 12:03:49 +0200
Subject: [PATCH 15/22] Refinements for small item arrangement using the
 increased cpu power.

---
 xs/src/libslic3r/ModelArrange.hpp | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp
index 618230cb7..b1ccf4d13 100644
--- a/xs/src/libslic3r/ModelArrange.hpp
+++ b/xs/src/libslic3r/ModelArrange.hpp
@@ -130,6 +130,7 @@ objfunc(const PointImpl& bincenter,
         double norm,            // A norming factor for physical dimensions
         // a spatial index to quickly get neighbors of the candidate item
         const SpatIndex& spatindex,
+        const SpatIndex& smalls_spatindex,
         const ItemGroup& remaining
         )
 {
@@ -161,7 +162,7 @@ objfunc(const PointImpl& bincenter,
     // Will hold the resulting score
     double score = 0;
 
-    if(isBig(item.area())) {
+    if(isBig(item.area()) || spatindex.empty()) {
         // This branch is for the bigger items..
 
         auto minc = ibb.minCorner(); // bottom left corner
@@ -183,6 +184,8 @@ objfunc(const PointImpl& bincenter,
 
         // The smalles distance from the arranged pile center:
         auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
+        auto bindist = pl::distance(ibb.center(), bincenter) / norm;
+        dist = 0.8*dist + 0.2*bindist;
 
         // Density is the pack density: how big is the arranged pile
         double density = 0;
@@ -207,14 +210,20 @@ objfunc(const PointImpl& bincenter,
             // candidate to be aligned with only one item.
             auto alignment_score = 1.0;
 
-            density = (fullbb.width()*fullbb.height()) / (norm*norm);
+            density = std::sqrt((fullbb.width() / norm )*
+                                (fullbb.height() / norm));
             auto querybb = item.boundingBox();
 
             // Query the spatial index for the neighbors
             std::vector<SpatElement> result;
             result.reserve(spatindex.size());
-            spatindex.query(bgi::intersects(querybb),
-                            std::back_inserter(result));
+            if(isBig(item.area())) {
+                spatindex.query(bgi::intersects(querybb),
+                                std::back_inserter(result));
+            } else {
+                smalls_spatindex.query(bgi::intersects(querybb),
+                                       std::back_inserter(result));
+            }
 
             for(auto& e : result) { // now get the score for the best alignment
                 auto idx = e.second;
@@ -235,12 +244,8 @@ objfunc(const PointImpl& bincenter,
             if(result.empty())
                 score = 0.5 * dist + 0.5 * density;
             else
-                score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
+                score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score;
         }
-    } else if( !isBig(item.area()) && spatindex.empty()) {
-        auto bindist = pl::distance(ibb.center(), bincenter) / norm;
-        // Bindist is surprisingly enough...
-        score = bindist;
     } else {
         // Here there are the small items that should be placed around the
         // already processed bigger items.
@@ -291,6 +296,7 @@ protected:
     PConfig pconf_; // Placement configuration
     double bin_area_;
     SpatIndex rtree_;
+    SpatIndex smallsrtree_;
     double norm_;
     Pile merged_pile_;
     Box pilebb_;
@@ -317,6 +323,7 @@ public:
             pilebb_ = sl::boundingBox(merged_pile);
 
             rtree_.clear();
+            smallsrtree_.clear();
 
             // We will treat big items (compared to the print bed) differently
             auto isBig = [this](double a) {
@@ -326,6 +333,7 @@ public:
             for(unsigned idx = 0; idx < items.size(); ++idx) {
                 Item& itm = items[idx];
                 if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
+                smallsrtree_.insert({itm.boundingBox(), idx});
             }
         };
 
@@ -357,6 +365,7 @@ public:
                                   bin_area_,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
 
             double score = std::get<0>(result);
@@ -393,6 +402,7 @@ public:
                                   bin_area_,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
 
             double score = std::get<0>(result);
@@ -435,6 +445,7 @@ public:
                                   bin_area_,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
             double score = std::get<0>(result);
 
@@ -462,6 +473,7 @@ public:
                                   0,
                                   norm_,
                                   rtree_,
+                                  smallsrtree_,
                                   remaining_);
             return std::get<0>(result);
         };

From 6de8e211317578e71398170fd0b4d215d9cc2781 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 7 Sep 2018 14:53:42 +0200
Subject: [PATCH 16/22] New support settings: "support_material_auto" controls,
 whether the supports shall be generated automatically. If not, only supports
 inside support enforcers are generated.

---
 xs/src/libslic3r/EdgeGrid.cpp        |  4 +-
 xs/src/libslic3r/EdgeGrid.hpp        |  2 +-
 xs/src/libslic3r/PrintConfig.cpp     |  8 +++
 xs/src/libslic3r/PrintConfig.hpp     |  3 ++
 xs/src/libslic3r/PrintObject.cpp     |  1 +
 xs/src/libslic3r/SupportMaterial.cpp | 81 +++++++++++++++-------------
 xs/src/slic3r/GUI/Preset.cpp         |  2 +-
 xs/src/slic3r/GUI/Tab.cpp            |  5 +-
 8 files changed, 63 insertions(+), 43 deletions(-)

diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 804b526c7..801a27e3a 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1225,7 +1225,7 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
 	return true;
 }
 
-Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
+Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
 {
 	assert(std::abs(2 * offset) < m_resolution);
 
@@ -1239,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
 			cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
 	// Fill in empty cells, which have a left / right neighbor filled.
 	// Fill in empty cells, which have the top / bottom neighbor filled.
-	{
+	if (fill_holes) {
 		std::vector<char> cell_inside2(cell_inside);
 		for (int r = 1; r + 1 < int(cell_rows); ++ r) {
 			for (int c = 1; c + 1 < int(cell_cols); ++ c) {
diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp
index 3eb741865..ab1aa4ed0 100644
--- a/xs/src/libslic3r/EdgeGrid.hpp
+++ b/xs/src/libslic3r/EdgeGrid.hpp
@@ -58,7 +58,7 @@ public:
 	const size_t		cols() const { return m_cols; }
 
 	// For supports: Contours enclosing the rasterized edges.
-	Polygons 			contours_simplified(coord_t offset) const;
+	Polygons 			contours_simplified(coord_t offset, bool fill_holes) const;
 
 protected:
 	struct Cell {
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 1a8b19c3f..32c3f5700 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1696,6 +1696,14 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "support-material!";
     def->default_value = new ConfigOptionBool(false);
 
+    def = this->add("support_material_auto", coBool);
+    def->label = L("Auto generated supports");
+    def->category = L("Support material");
+    def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
+                     " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
+    def->cli = "support-material-auto!";
+    def->default_value = new ConfigOptionBool(true);
+
     def = this->add("support_material_xy_spacing", coFloatOrPercent);
     def->label = L("XY separation between an object and its support");
     def->category = L("Support material");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 99959cebb..89ba81124 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -331,6 +331,8 @@ public:
 //    ConfigOptionFloat               seam_preferred_direction;
 //    ConfigOptionFloat               seam_preferred_direction_jitter;
     ConfigOptionBool                support_material;
+    // Automatic supports (generated based on support_material_threshold).
+    ConfigOptionBool                support_material_auto;
     // Direction of the support pattern (in XY plane).
     ConfigOptionFloat               support_material_angle;
     ConfigOptionBool                support_material_buildplate_only;
@@ -372,6 +374,7 @@ protected:
 //        OPT_PTR(seam_preferred_direction);
 //        OPT_PTR(seam_preferred_direction_jitter);
         OPT_PTR(support_material);
+        OPT_PTR(support_material_auto);
         OPT_PTR(support_material_angle);
         OPT_PTR(support_material_buildplate_only);
         OPT_PTR(support_material_contact_distance);
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 6dd764ae1..ea6b39280 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -172,6 +172,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
             steps.emplace_back(posSlice);
         } else if (
                opt_key == "support_material"
+            || opt_key == "support_material_auto"
             || opt_key == "support_material_angle"
             || opt_key == "support_material_buildplate_only"
             || opt_key == "support_material_enforce_layers"
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index e1a8995a6..9019583b9 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -495,12 +495,12 @@ public:
     // and trim the extracted polygons by trimming_polygons.
     // Trimming by the trimming_polygons may split the extracted polygons into pieces.
     // Remove all the pieces, which do not contain any of the island_samples.
-    Polygons extract_support(const coord_t offset_in_grid)
+    Polygons extract_support(const coord_t offset_in_grid, bool fill_holes)
     {
         // Generate islands, so each island may be tested for overlap with m_island_samples.
         assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
         ExPolygons islands = diff_ex(
-            m_grid.contours_simplified(offset_in_grid),
+            m_grid.contours_simplified(offset_in_grid, fill_holes),
             *m_trimming_polygons, false);
 
         // Extract polygons, which contain some of the m_island_samples.
@@ -812,6 +812,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     // Output layers, sorted by top Z.
     MyLayersPtr contact_out;
 
+    const bool   support_auto  = m_object_config->support_material_auto.value;
     // If user specified a custom angle threshold, convert it to radians.
     // Zero means automatic overhang detection.
     const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ? 
@@ -851,7 +852,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
     contact_out.assign(num_layers * 2, nullptr);
     tbb::spin_mutex layer_storage_mutex;
     tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
-        [this, &object, &buildplate_covered, &enforcers, &blockers, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
+        [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out]
+        (const tbb::blocked_range<size_t>& range) {
             for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) 
             {
                 const Layer &layer = *object.layers[layer_id];
@@ -901,34 +903,35 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                 diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
                             }
                         } else {
-                            // Get the regions needing a suport, collapse very tiny spots.
-                            //FIXME cache the lower layer offset if this layer has multiple regions.
-#if 1
-                            diff_polygons = offset2(
-                                diff(layerm_polygons,
-                                     offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
-                                //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
-                                // no support at all for not so steep overhangs.
-                                - 0.1f * fw, 0.1f * fw);
-#else
-                            diff_polygons = 
-                                diff(layerm_polygons,
-                                     offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
-#endif
-                            if (! buildplate_covered.empty()) {
-                                // Don't support overhangs above the top surfaces.
-                                // This step is done before the contact surface is calculated by growing the overhang region.
-                                diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+                            if (support_auto) {
+                                // Get the regions needing a suport, collapse very tiny spots.
+                                //FIXME cache the lower layer offset if this layer has multiple regions.
+    #if 1
+                                diff_polygons = offset2(
+                                    diff(layerm_polygons,
+                                         offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), 
+                                    //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
+                                    // no support at all for not so steep overhangs.
+                                    - 0.1f * fw, 0.1f * fw);
+    #else
+                                diff_polygons = 
+                                    diff(layerm_polygons,
+                                         offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+    #endif
+                                if (! buildplate_covered.empty()) {
+                                    // Don't support overhangs above the top surfaces.
+                                    // This step is done before the contact surface is calculated by growing the overhang region.
+                                    diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+                                }
+                                if (! diff_polygons.empty()) {
+    	                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
+    	                            // This is done to increase size of the supporting columns below, as they are calculated by 
+    	                            // propagating these contact surfaces downwards.
+    	                            diff_polygons = diff(
+    	                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), 
+    	                                lower_layer_polygons);
+    							}
                             }
-                            if (! diff_polygons.empty()) {
-	                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
-	                            // This is done to increase size of the supporting columns below, as they are calculated by 
-	                            // propagating these contact surfaces downwards.
-	                            diff_polygons = diff(
-	                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), 
-	                                lower_layer_polygons);
-							}
-
                             if (! enforcers.empty()) {
                                 // Apply the "support enforcers".
                                 //FIXME add the "enforcers" to the sparse support regions only.
@@ -1000,7 +1003,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                 slices_margin_cached_offset = slices_margin_offset;
                                 slices_margin_cached = (slices_margin_offset == 0.f) ? 
                                     lower_layer_polygons :
-                                    offset2(to_polygons(lower_layer.slices.expolygons), - scale_(- no_interface_offset * 0.5f), slices_margin_offset + scale_(- no_interface_offset * 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS);
+                                    offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
                                 if (! buildplate_covered.empty()) {
                                     // Trim the inflated contact surfaces by the top surfaces as well.
                                     polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -1102,16 +1105,18 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                         m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
                         Geometry::deg2rad(m_object_config->support_material_angle.value));
                     // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
-                    new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3));
+                    new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
                     // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
                     if (layer_id == 0) {
                     // if (no_interface_offset == 0.f) {
-                        new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
+                        new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
                     } else  {
-                        //Polygons dense_interface_polygons = diff(overhang_polygons, offset(lower_layer_polygons, scale_(no_interface_offset * 0.7f)));
                         Polygons dense_interface_polygons = diff(overhang_polygons, 
-                            offset2(lower_layer_polygons, scale_(- no_interface_offset * 0.5f), scale_(no_interface_offset * (0.7f + 0.5f)), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+                            offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+//                            offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                         if (! dense_interface_polygons.empty()) {
+                            //FIXME do it for non-soluble support interfaces only.
+                            //FIXME do it for the bridges only?
                             SupportGridPattern support_grid_pattern(
                                 // Support islands, to be stretched into a grid.
                                 dense_interface_polygons, 
@@ -1120,7 +1125,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                 // Grid resolution.
                                 m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
                                 Geometry::deg2rad(m_object_config->support_material_angle.value));                        
-                            new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
+                            new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
                         }
                     }
 
@@ -1407,7 +1412,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                     , &layer
         #endif /* SLIC3R_DEBUG */
                     ] {
-                    layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25);
+                    layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true);
         #ifdef SLIC3R_DEBUG
                     Slic3r::SVG::export_expolygons(
                         debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1421,7 +1426,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
                     , &layer
         #endif /* SLIC3R_DEBUG */
                     ] {
-                    projection_new = support_grid_pattern.extract_support(-5);
+                    projection_new = support_grid_pattern.extract_support(-5, true);
         #ifdef SLIC3R_DEBUG
                     Slic3r::SVG::export_expolygons(
                         debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index af2cd8ff6..cede4c707 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -292,7 +292,7 @@ const std::vector<std::string>& Preset::print_options()
         "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
         "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", 
         "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
-        "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", 
+        "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", 
         "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", 
         "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", 
         "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 6505e1092..9d265cfc4 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -847,6 +847,7 @@ void TabPrint::build()
 	page = add_options_page(_(L("Support material")), "building.png");
 		optgroup = page->new_optgroup(_(L("Support material")));
 		optgroup->append_single_option_line("support_material");
+		optgroup->append_single_option_line("support_material_auto");
 		optgroup->append_single_option_line("support_material_threshold");
 		optgroup->append_single_option_line("support_material_enforce_layers");
 
@@ -1183,13 +1184,15 @@ void TabPrint::update()
 
 	bool have_raft = m_config->opt_int("raft_layers") > 0;
 	bool have_support_material = m_config->opt_bool("support_material") || have_raft;
+	bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");
 	bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
 	bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
-	for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
+	for (auto el : {"support_material_pattern", "support_material_with_sheath",
 					"support_material_spacing", "support_material_angle", "support_material_interface_layers",
 					"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
 					"support_material_xy_spacing" })
 		get_field(el)->toggle(have_support_material);
+	get_field("support_material_threshold")->toggle(have_support_material_auto);
 
 	for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
 					"support_material_interface_speed", "support_material_interface_contact_loops" })

From 712fef0669a491c4335286d61d34ebb0b24345e6 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 12 Sep 2018 09:28:26 +0200
Subject: [PATCH 17/22] Added number of toolchanges into 'Sliced info'
 statistics

---
 lib/Slic3r/GUI/Plater.pm                    | 12 ++++++++----
 xs/src/libslic3r/GCode/WipeTower.hpp        |  3 +++
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  2 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  5 +++--
 xs/src/libslic3r/Print.cpp                  |  1 +
 xs/src/libslic3r/Print.hpp                  |  1 +
 xs/xsp/Print.xsp                            | 10 ++++++++++
 7 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 56c21bbbb..469fa42dd 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1646,14 +1646,15 @@ sub print_info_box_show {
         $grid_sizer->AddGrowableCol(1, 1);
         $grid_sizer->AddGrowableCol(3, 1);
         $print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
+        my $is_wipe_tower = $self->{print}->total_wipe_tower_filament > 0;
         my @info = (
             L("Used Filament (m)")
-                => $self->{print}->total_wipe_tower_filament > 0 ?
+                =>  $is_wipe_tower ?
                        sprintf("%.2f  (%.2f %s + %.2f %s)" , $self->{print}->total_used_filament / 1000,
                                                             ($self->{print}->total_used_filament - $self->{print}->total_wipe_tower_filament) / 1000,
                                                              L("objects"),
 							     $self->{print}->total_wipe_tower_filament / 1000,
-                                                             L("wipe_tower")) :
+                                                             L("wipe tower")) :
                        sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
 
             L("Used Filament (mm³)")
@@ -1661,18 +1662,21 @@ sub print_info_box_show {
             L("Used Filament (g)"),
                 => sprintf("%.2f" , $self->{print}->total_weight),
             L("Cost"),
-                => $self->{print}->total_wipe_tower_cost > 0 ?
+                => $is_wipe_tower ?
                        sprintf("%.2f  (%.2f %s + %.2f %s)" , $self->{print}->total_cost,
                                                             ($self->{print}->total_cost - $self->{print}->total_wipe_tower_cost),
                                                              L("objects"),
 							     $self->{print}->total_wipe_tower_cost,
-                                                             L("wipe_tower")) :
+                                                             L("wipe tower")) :
                        sprintf("%.2f" , $self->{print}->total_cost),
             L("Estimated printing time (normal mode)")
                 => $self->{print}->estimated_normal_print_time,
             L("Estimated printing time (silent mode)")
                 => $self->{print}->estimated_silent_print_time
         );
+        # if there is a wipe tower, insert number of toolchanges info into the array:
+        splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->m_wipe_tower_number_of_toolchanges))  if ($is_wipe_tower);
+
         while ( my $label = shift @info) {
             my $value = shift @info;
             next if $value eq "N/A";
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index e7cd8ea1a..21c10969a 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -158,6 +158,9 @@ public:
 
     // Returns used filament length per extruder:
     virtual std::vector<float> get_used_filament() const = 0;
+
+    // Returns total number of toolchanges:
+    virtual int get_number_of_toolchanges() const = 0;
 };
 
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 23a2bef9b..0427e32d6 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -613,10 +613,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
         toolchange_Load(writer, cleaning_box);
         writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
         toolchange_Wipe(writer, cleaning_box, wipe_volume);     // Wipe the newly loaded filament until the end of the assigned wipe area.
+        ++ m_num_tool_changes;
     } else
         toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
-    ++ m_num_tool_changes;
     m_depth_traversed += wipe_area;
 
     if (last_change_in_layer) {// draw perimeter line
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 964ee0039..06625d189 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -46,7 +46,7 @@ public:
 	WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
                      float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
                      const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
-		m_wipe_tower_pos(x, y),
+    m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
 		m_wipe_tower_rotation_angle(rotation_angle),
 		m_y_shift(0.f),
@@ -174,7 +174,8 @@ public:
 		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
 	}
 
-    virtual std::vector<float> get_used_filament() const { return m_used_filament_length; }
+    virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
+    virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
 
 
 private:
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index ba8abd040..eb2112ef0 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1195,6 +1195,7 @@ void Print::_make_wipe_tower()
 		wipe_tower.tool_change((unsigned int)-1, false));
 
     m_wipe_tower_used_filament = wipe_tower.get_used_filament();
+    m_wipe_tower_number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
 }
 
 std::string Print::output_filename()
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 537070a34..95b8abc5b 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -310,6 +310,7 @@ public:
     std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
     std::unique_ptr<WipeTower::ToolChangeResult>          m_wipe_tower_final_purge;
     std::vector<float>                                    m_wipe_tower_used_filament;
+    int                                                   m_wipe_tower_number_of_toolchanges = -1;
 
     std::string output_filename();
     std::string output_filepath(const std::string &path);
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index 50f899f2c..1dee8a4c4 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -296,6 +296,16 @@ Print::total_wipe_tower_filament(...)
             THIS->total_wipe_tower_filament = (double)SvNV(ST(1));
         }
         RETVAL = THIS->total_wipe_tower_filament;
+    OUTPUT:
+        RETVAL
+
+int
+Print::m_wipe_tower_number_of_toolchanges(...)
+    CODE:
+        if (items > 1) {
+            THIS->m_wipe_tower_number_of_toolchanges = (double)SvNV(ST(1));
+        }
+        RETVAL = THIS->m_wipe_tower_number_of_toolchanges;
     OUTPUT:
         RETVAL        
 %}

From 646e991d4b6907a2bcba40073e7895f63522d86d Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Mon, 3 Sep 2018 16:19:16 +0200
Subject: [PATCH 18/22] ConfigWizard: Properly apply gcode_flavor Fixes #1138

---
 xs/src/slic3r/GUI/ConfigWizard.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp
index 9f736ded8..bd9b9328a 100644
--- a/xs/src/slic3r/GUI/ConfigWizard.cpp
+++ b/xs/src/slic3r/GUI/ConfigWizard.cpp
@@ -409,11 +409,10 @@ PageFirmware::PageFirmware(ConfigWizard *parent) :
 
 void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
 {
-	ConfigOptionEnum<GCodeFlavor> opt;
-
 	auto sel = gcode_picker->GetSelection();
-	if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) {
-		config.set_key_value("gcode_flavor", &opt);
+	if (sel >= 0 && sel < gcode_opt.enum_labels.size()) {
+		auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
+		config.set_key_value("gcode_flavor", opt);
 	}
 }
 

From a8c28e210ddd4ebfdd64c4015fdf688044513542 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 4 Sep 2018 10:40:28 +0200
Subject: [PATCH 19/22] Fix CMake string comparison issue Fixes #1187

---
 xs/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index efd52ac13..d19aad70a 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -503,7 +503,7 @@ if (WIN32)
 endif ()
 
 # SLIC3R_MSVC_PDB
-if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release")
+if (MSVC AND SLIC3R_MSVC_PDB AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release")
     set_target_properties(XS PROPERTIES
         COMPILE_FLAGS "/Zi"
         LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"

From 8988e8cf0ac831307871eae226c77aedd7d06191 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 4 Sep 2018 11:36:58 +0200
Subject: [PATCH 20/22] Firmware updater: Fix MMU2 lookup wrt. other Prusa
 devices being connected

---
 xs/src/slic3r/GUI/FirmwareDialog.cpp | 22 ++++++++++++++--------
 xs/src/slic3r/Utils/Serial.cpp       |  7 ++++++-
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index d0cd9f8cf..0c06e517e 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -367,7 +367,7 @@ void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries)
 
 		auto ports = Utils::scan_serial_ports_extended();
 		ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
-			return port.id_vendor != USB_VID_PRUSA && port.id_product != USB_PID_MMU_BOOT;
+			return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT;
 		}), ports.end());
 
 		if (ports.size() == 1) {
@@ -390,23 +390,22 @@ void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port)
 
 void FirmwareDialog::priv::lookup_port_mmu()
 {
+	static const auto msg_not_found =
+		"The Multi Material Control device was not found.\n"
+		"If the device is connected, please press the Reset button next to the USB connector ...";
+
 	BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
 
 	auto ports = Utils::scan_serial_ports_extended();
 	ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
-		return port.id_vendor != USB_VID_PRUSA &&
+		return port.id_vendor != USB_VID_PRUSA ||
 			port.id_product != USB_PID_MMU_BOOT &&
 			port.id_product != USB_PID_MMU_APP;
 	}), ports.end());
 
 	if (ports.size() == 0) {
 		BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
-
-		queue_status(_(L(
-			"The Multi Material Control device was not found.\n"
-			"If the device is connected, please press the Reset button next to the USB connector ..."
-		)));
-
+		queue_status(_(L(msg_not_found)));
 		wait_for_mmu_bootloader(30);
 	} else if (ports.size() > 1) {
 		BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
@@ -417,6 +416,13 @@ void FirmwareDialog::priv::lookup_port_mmu()
 			BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
 			mmu_reboot(ports[0]);
 			wait_for_mmu_bootloader(10);
+
+			if (! port) {
+				// The device in bootloader mode was not found, inform the user and wait some more...
+				BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ...";
+				queue_status(_(L(msg_not_found)));
+				wait_for_mmu_bootloader(30);
+			}
 		} else {
 			port = ports[0];
 		}
diff --git a/xs/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp
index 183119b44..601719b50 100644
--- a/xs/src/slic3r/Utils/Serial.cpp
+++ b/xs/src/slic3r/Utils/Serial.cpp
@@ -231,7 +231,12 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
                 spi.port = path;
 #ifdef __linux__
 				auto friendly_name = sysfs_tty_prop(name, "product");
-				spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path;
+				if (friendly_name) {
+					spi.is_printer = looks_like_printer(*friendly_name);
+					spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str();
+				} else {
+					spi.friendly_name = path;
+				}
 				auto vid = sysfs_tty_prop_hex(name, "idVendor");
 				auto pid = sysfs_tty_prop_hex(name, "idProduct");
 				if (vid && pid) {

From 7258c597b9140f09879db12ffc7334135b2d72d8 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Mon, 6 Aug 2018 18:26:29 +0200
Subject: [PATCH 21/22] Fix window size persistence Fixes #1116 Fixes #1175

---
 lib/Slic3r/GUI.pm                             | 24 --------
 lib/Slic3r/GUI/MainFrame.pm                   |  4 +-
 lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm |  4 +-
 xs/src/slic3r/GUI/AppConfig.cpp               |  8 +++
 xs/src/slic3r/GUI/AppConfig.hpp               |  8 +++
 xs/src/slic3r/GUI/ConfigWizard.cpp            |  9 +--
 xs/src/slic3r/GUI/GUI.cpp                     | 57 ++++++++++++++++++-
 xs/src/slic3r/GUI/GUI.hpp                     |  8 ++-
 xs/xsp/GUI.xsp                                |  6 ++
 9 files changed, 93 insertions(+), 35 deletions(-)

diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 483fd36f9..7510d22af 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -356,28 +356,4 @@ sub set_menu_item_icon {
     }
 }
 
-sub save_window_pos {
-    my ($self, $window, $name) = @_;
-    
-    $self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY);
-    $self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH);
-    $self->{app_config}->set("${name}_maximized", $window->IsMaximized);
-    $self->{app_config}->save;
-}
-
-sub restore_window_pos {
-    my ($self, $window, $name) = @_;
-    if ($self->{app_config}->has("${name}_pos")) {
-        my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ];
-        $window->SetSize($size);
-        
-        my $display = Wx::Display->new->GetClientArea();
-        my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ];
-        if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) {
-            $window->Move($pos);
-        }
-        $window->Maximize(1) if $self->{app_config}->get("${name}_maximized");
-    }
-}
-
 1;
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index 6baefa545..9d2fd19ce 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -88,7 +88,7 @@ sub new {
         $self->Fit;
         $self->SetMinSize([760, 490]);
         $self->SetSize($self->GetMinSize);
-        wxTheApp->restore_window_pos($self, "main_frame");
+        Slic3r::GUI::restore_window_size($self, "main_frame");
         $self->Show;
         $self->Layout;
     }
@@ -101,7 +101,7 @@ sub new {
             return;
         }
         # save window size
-        wxTheApp->save_window_pos($self, "main_frame");
+        Slic3r::GUI::save_window_size($self, "main_frame");
         # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
         # but in rare cases it may not have been called yet.
         wxTheApp->{app_config}->save;
diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
index 3befba708..3ccf1d7f8 100644
--- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
@@ -33,7 +33,7 @@ sub new {
         $self->{layers}->Closing;
         
         # save window size
-        wxTheApp->save_window_pos($self, "object_settings");
+        Slic3r::GUI::save_window_size($self, "object_settings");
         
         $self->EndModal(wxID_OK);
         $self->{parts}->Destroy;
@@ -49,7 +49,7 @@ sub new {
     
     $self->Layout;
     
-    wxTheApp->restore_window_pos($self, "object_settings");
+    Slic3r::GUI::restore_window_size($self, "object_settings");
     
     return $self;
 }
diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp
index 2a33cd733..e66221351 100644
--- a/xs/src/slic3r/GUI/AppConfig.cpp
+++ b/xs/src/slic3r/GUI/AppConfig.cpp
@@ -60,6 +60,14 @@ void AppConfig::set_defaults()
 
     if (get("remember_output_path").empty())
         set("remember_output_path", "1");
+
+    // Remove legacy window positions/sizes
+    erase("", "main_frame_maximized");
+    erase("", "main_frame_pos");
+    erase("", "main_frame_size");
+    erase("", "object_settings_maximized");
+    erase("", "object_settings_pos");
+    erase("", "object_settings_size");
 }
 
 void AppConfig::load()
diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp
index b742176ed..5af635a12 100644
--- a/xs/src/slic3r/GUI/AppConfig.hpp
+++ b/xs/src/slic3r/GUI/AppConfig.hpp
@@ -72,6 +72,14 @@ public:
 	bool				has(const std::string &key) const
 		{ return this->has("", key); }
 
+	void				erase(const std::string &section, const std::string &key)
+	{
+		auto it = m_storage.find(section);
+		if (it != m_storage.end()) {
+			it->second.erase(key);
+		}
+	}
+
 	void 				clear_section(const std::string &section)
 		{ m_storage[section].clear(); }
 
diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp
index bd9b9328a..e784d8525 100644
--- a/xs/src/slic3r/GUI/ConfigWizard.cpp
+++ b/xs/src/slic3r/GUI/ConfigWizard.cpp
@@ -870,10 +870,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
 	// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
 	auto wizard_size = GetSize();
 	unsigned width, height;
-	GUI::get_current_screen_size(width, height);
-	wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
-	wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
-	SetMinSize(wizard_size);
+	if (GUI::get_current_screen_size(this, width, height)) {
+		wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
+		wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
+		SetMinSize(wizard_size);
+	}
 	Fit();
 
 	p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 8555f0b92..0272aae4e 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -7,6 +7,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
 
 #if __APPLE__
 #import <IOKit/pwr_mgt/IOPMLib.h>
@@ -974,14 +975,66 @@ int get_export_option(wxFileDialog* dlg)
 
 }
 
-void get_current_screen_size(unsigned &width, unsigned &height)
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
 {
-	wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame));
+	const auto idx = wxDisplay::GetFromWindow(window);
+	if (idx == wxNOT_FOUND) {
+		return false;
+	}
+
+	wxDisplay display(idx);
 	const auto disp_size = display.GetClientArea();
 	width = disp_size.GetWidth();
 	height = disp_size.GetHeight();
+
+	return true;
 }
 
+void save_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+	const wxSize size = window->GetSize();
+	const wxPoint pos = window->GetPosition();
+	const auto maximized = window->IsMaximized() ? "1" : "0";
+
+	g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
+	g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized);
+}
+
+void restore_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+	// XXX: This still doesn't behave nicely in some situations (mostly on Linux).
+	// The problem is that it's hard to obtain window position with respect to screen geometry reliably
+	// from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
+	// it's actually visible. I suspect this has something to do with window initialization (maybe we
+	// restore window geometry too early), but haven't yet found a workaround.
+
+	const auto display_idx = wxDisplay::GetFromWindow(window);
+	if (display_idx == wxNOT_FOUND) { return; }
+
+	const auto display = wxDisplay(display_idx).GetClientArea();
+	std::vector<std::string> pair;
+
+	try {
+		const auto key_size = (boost::format("window_%1%_size") % name).str();
+		if (g_AppConfig->has(key_size)) {
+			if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) {
+				auto width = boost::lexical_cast<int>(pair[0]);
+				auto height = boost::lexical_cast<int>(pair[1]);
+
+				window->SetSize(width, height);
+			}
+		}
+	} catch(const boost::bad_lexical_cast &) {}
+
+	// Maximizing should be the last thing to do.
+	// This ensure the size and position are sane when the user un-maximizes the window.
+	const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
+	if (g_AppConfig->get(key_maximized) == "1") {
+		window->Maximize(true);
+	}
+}
+
+
 void about()
 {
     AboutDialog dlg;
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 165288819..68dbdfe84 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -24,6 +24,7 @@ class wxBoxSizer;
 class wxFlexGridSizer;
 class wxButton;
 class wxFileDialog;
+class wxTopLevelWindow;
 
 namespace Slic3r { 
 
@@ -182,7 +183,12 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
 int get_export_option(wxFileDialog* dlg);
 
 // Returns the dimensions of the screen on which the main frame is displayed
-void get_current_screen_size(unsigned &width, unsigned &height);
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
+
+// Save window size and maximized status into AppConfig
+void save_window_size(wxTopLevelWindow *window, const std::string &name);
+// Restore the above
+void restore_window_size(wxTopLevelWindow *window, const std::string &name);
 
 // Display an About dialog
 extern void about();
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index c6eead1ad..8d2efb858 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -111,3 +111,9 @@ void register_on_request_update_callback(SV* callback)
 void deregister_on_request_update_callback()
     %code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %};
 
+void save_window_size(SV *window, std::string name)
+    %code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
+
+void restore_window_size(SV *window, std::string name)
+    %code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
+

From 61a6aa86922cf1a123dfef14e525fd2d9ee42fe1 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Thu, 6 Sep 2018 16:45:31 +0200
Subject: [PATCH 22/22] FirmwareDialog: Add appropriate set of wildcards to the
 file picker

---
 xs/src/slic3r/GUI/FirmwareDialog.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index 0c06e517e..d5ac64d90 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -708,7 +708,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
 	panel->SetSizer(vsizer);
 
 	auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
-	p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
+	p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr, 
+		"Hex files (*.hex)|*.hex|All files|*.*");
 
 	auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
 	p->port_picker = new wxComboBox(panel, wxID_ANY);