Browse Source

Integrated the new layer height spans with configs into the backend.
Fixed some compiler warnings.

bubnikv 5 years ago
parent
commit
35b3fd3176

+ 6 - 6
src/libslic3r/Fill/Fill.cpp

@@ -126,7 +126,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
         Polygons surfaces_polygons = to_polygons(surfaces);
         Polygons surfaces_polygons = to_polygons(surfaces);
         Polygons collapsed = diff(
         Polygons collapsed = diff(
             surfaces_polygons,
             surfaces_polygons,
-            offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
+            offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
             true);
             true);
         Polygons to_subtract;
         Polygons to_subtract;
         to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
         to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
@@ -137,7 +137,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
         surfaces_append(
         surfaces_append(
             surfaces,
             surfaces,
             intersection_ex(
             intersection_ex(
-                offset(collapsed, distance_between_surfaces),
+                offset(collapsed, (float)distance_between_surfaces),
                 to_subtract,
                 to_subtract,
                 true),
                 true),
             stInternalSolid);
             stInternalSolid);
@@ -219,14 +219,14 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
         f->z = layerm.layer()->print_z;
         f->z = layerm.layer()->print_z;
         f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
         f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
         // Maximum length of the perimeter segment linking two infill lines.
         // Maximum length of the perimeter segment linking two infill lines.
-        f->link_max_length = scale_(link_max_length);
+        f->link_max_length = (coord_t)scale_(link_max_length);
         // Used by the concentric infill pattern to clip the loops to create extrusion paths.
         // Used by the concentric infill pattern to clip the loops to create extrusion paths.
-        f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
+        f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
 //        f->layer_height = h;
 //        f->layer_height = h;
 
 
         // apply half spacing using this flow's own spacing and generate infill
         // apply half spacing using this flow's own spacing and generate infill
         FillParams params;
         FillParams params;
-        params.density = 0.01 * density;
+        params.density = float(0.01 * density);
 //        params.dont_adjust = true;
 //        params.dont_adjust = true;
         params.dont_adjust = false;
         params.dont_adjust = false;
         Polylines polylines = f->fill_surface(&surface, params);
         Polylines polylines = f->fill_surface(&surface, params);
@@ -240,7 +240,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
             // so we can safely ignore the slight variation that might have
             // so we can safely ignore the slight variation that might have
             // been applied to $f->flow_spacing
             // been applied to $f->flow_spacing
         } else {
         } else {
-            flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
+            flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow());
         }
         }
 
 
         // Save into layer.
         // Save into layer.

+ 2 - 2
src/libslic3r/Format/3mf.cpp

@@ -2031,7 +2031,7 @@ namespace Slic3r {
                 return false;
                 return false;
             }
             }
 
 
-            vertices_count += its.vertices.size();
+            vertices_count += (int)its.vertices.size();
 
 
             const Transform3d& matrix = volume->get_matrix();
             const Transform3d& matrix = volume->get_matrix();
 
 
@@ -2061,7 +2061,7 @@ namespace Slic3r {
 
 
             // updates triangle offsets
             // updates triangle offsets
             volume_it->second.first_triangle_id = triangles_count;
             volume_it->second.first_triangle_id = triangles_count;
-            triangles_count += its.indices.size();
+            triangles_count += (int)its.indices.size();
             volume_it->second.last_triangle_id = triangles_count - 1;
             volume_it->second.last_triangle_id = triangles_count - 1;
 
 
             for (size_t i = 0; i < its.indices.size(); ++ i)
             for (size_t i = 0; i < its.indices.size(); ++ i)

+ 6 - 6
src/libslic3r/Format/AMF.cpp

@@ -192,7 +192,7 @@ struct AMFParserContext
     };
     };
 
 
     // Version of the amf file
     // Version of the amf file
-    unsigned int m_version;
+    unsigned int             m_version;
     // Current Expat XML parser instance.
     // Current Expat XML parser instance.
     XML_Parser               m_parser;
     XML_Parser               m_parser;
     // Model to receive objects extracted from an AMF file.
     // Model to receive objects extracted from an AMF file.
@@ -616,7 +616,7 @@ void AMFParserContext::endElement(const char * /* name */)
                     if (end != nullptr)
                     if (end != nullptr)
 	                    *end = 0;
 	                    *end = 0;
 
 
-                    point(coord_idx) = atof(p);
+                    point(coord_idx) = float(atof(p));
                     if (++coord_idx == 5) {
                     if (++coord_idx == 5) {
                         m_object->sla_support_points.push_back(sla::SupportPoint(point));
                         m_object->sla_support_points.push_back(sla::SupportPoint(point));
                         coord_idx = 0;
                         coord_idx = 0;
@@ -628,8 +628,8 @@ void AMFParserContext::endElement(const char * /* name */)
                 m_object->sla_points_status = sla::PointsStatus::UserModified;
                 m_object->sla_points_status = sla::PointsStatus::UserModified;
             }
             }
             else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE && 
             else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE && 
-                     m_object && strcmp(opt_key, "layer_height_ranges") == 0) {
-                // Parse object's layer_height_ranges, a semicolon separated doubles.
+                     m_object && strcmp(opt_key, "layer_height_range") == 0) {
+                // Parse object's layer_height_range, a semicolon separated doubles.
                 char* p = const_cast<char*>(m_value[1].c_str());
                 char* p = const_cast<char*>(m_value[1].c_str());
                 char* end = strchr(p, ';');
                 char* end = strchr(p, ';');
                 *end = 0;
                 *end = 0;
@@ -946,7 +946,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
             for (auto range : config_ranges) {
             for (auto range : config_ranges) {
                 stream << "      <range id=\"" << layer_counter << "\">\n";
                 stream << "      <range id=\"" << layer_counter << "\">\n";
 
 
-                stream << "        <metadata type=\"slic3r.layer_height_ranges\">";
+                stream << "        <metadata type=\"slic3r.layer_height_range\">";
                 stream << range.first.first << ";" << range.first.second << "</metadata>\n";
                 stream << range.first.first << ";" << range.first.second << "</metadata>\n";
 
 
                 for (const std::string& key : range.second.keys())
                 for (const std::string& key : range.second.keys())
@@ -994,7 +994,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
                 stream << "           </coordinates>\n";
                 stream << "           </coordinates>\n";
                 stream << "         </vertex>\n";
                 stream << "         </vertex>\n";
             }
             }
-            num_vertices += its.vertices.size();
+            num_vertices += (int)its.vertices.size();
         }
         }
         stream << "      </vertices>\n";
         stream << "      </vertices>\n";
         for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
         for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {

+ 1 - 3
src/libslic3r/Model.cpp

@@ -593,7 +593,6 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
     this->config                      = rhs.config;
     this->config                      = rhs.config;
     this->sla_support_points          = rhs.sla_support_points;
     this->sla_support_points          = rhs.sla_support_points;
     this->sla_points_status           = rhs.sla_points_status;
     this->sla_points_status           = rhs.sla_points_status;
-    this->layer_height_ranges         = rhs.layer_height_ranges;
     this->layer_config_ranges         = rhs.layer_config_ranges;    // #ys_FIXME_experiment
     this->layer_config_ranges         = rhs.layer_config_ranges;    // #ys_FIXME_experiment
     this->layer_height_profile        = rhs.layer_height_profile;
     this->layer_height_profile        = rhs.layer_height_profile;
     this->origin_translation          = rhs.origin_translation;
     this->origin_translation          = rhs.origin_translation;
@@ -630,7 +629,6 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
     this->config                      = std::move(rhs.config);
     this->config                      = std::move(rhs.config);
     this->sla_support_points          = std::move(rhs.sla_support_points);
     this->sla_support_points          = std::move(rhs.sla_support_points);
     this->sla_points_status           = std::move(rhs.sla_points_status);
     this->sla_points_status           = std::move(rhs.sla_points_status);
-    this->layer_height_ranges         = std::move(rhs.layer_height_ranges);
     this->layer_config_ranges         = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
     this->layer_config_ranges         = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
     this->layer_height_profile        = std::move(rhs.layer_height_profile);
     this->layer_height_profile        = std::move(rhs.layer_height_profile);
     this->origin_translation          = std::move(rhs.origin_translation);
     this->origin_translation          = std::move(rhs.origin_translation);
@@ -1809,7 +1807,7 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
         if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
         if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
             return true;
             return true;
 
 
-        ++i_old;
+        ++ i_old;
         ++ i_new;
         ++ i_new;
     }
     }
     for (; i_old < model_object_old.volumes.size(); ++ i_old) {
     for (; i_old < model_object_old.volumes.size(); ++ i_old) {

+ 2 - 4
src/libslic3r/Model.hpp

@@ -179,10 +179,8 @@ public:
     ModelVolumePtrs         volumes;
     ModelVolumePtrs         volumes;
     // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
     // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
     DynamicPrintConfig      config;
     DynamicPrintConfig      config;
-    // Variation of a layer thickness for spans of Z coordinates.
-    t_layer_height_ranges   layer_height_ranges;
-    // Variation of a layer thickness for spans of Z coordinates.
-    t_layer_config_ranges         layer_config_ranges;
+    // Variation of a layer thickness for spans of Z coordinates + optional parameter overrides.
+    t_layer_config_ranges   layer_config_ranges;
     // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
     // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
     // The pairs of <z, layer_height> are packed into a 1D array.
     // The pairs of <z, layer_height> are packed into a 1D array.
     std::vector<coordf_t>   layer_height_profile;
     std::vector<coordf_t>   layer_height_profile;

+ 189 - 121
src/libslic3r/Print.cpp

@@ -41,36 +41,6 @@ void Print::clear()
     m_model.clear_objects();
     m_model.clear_objects();
 }
 }
 
 
-// Only used by the Perl test cases.
-void Print::reload_object(size_t /* idx */)
-{
-	ModelObjectPtrs model_objects;
-	{
-		tbb::mutex::scoped_lock lock(this->state_mutex());
-        // The following call should stop background processing if it is running.
-        this->invalidate_all_steps();
-		/* TODO: this method should check whether the per-object config and per-material configs
-			have changed in such a way that regions need to be rearranged or we can just apply
-			the diff and invalidate something.  Same logic as apply()
-			For now we just re-add all objects since we haven't implemented this incremental logic yet.
-			This should also check whether object volumes (parts) have changed. */
-		// collect all current model objects
-		model_objects.reserve(m_objects.size());
-		for (PrintObject *object : m_objects)
-			model_objects.push_back(object->model_object());
-		// remove our print objects
-		for (PrintObject *object : m_objects)
-			delete object;
-		m_objects.clear();
-		for (PrintRegion *region : m_regions)
-			delete region;
-		m_regions.clear();
-	}
-	// re-add model objects
-    for (ModelObject *mo : model_objects)
-        this->add_model_object(mo);
-}
-
 PrintRegion* Print::add_region()
 PrintRegion* Print::add_region()
 {
 {
     m_regions.emplace_back(new PrintRegion(this));
     m_regions.emplace_back(new PrintRegion(this));
@@ -335,7 +305,7 @@ unsigned int Print::num_object_instances() const
 {
 {
 	unsigned int instances = 0;
 	unsigned int instances = 0;
     for (const PrintObject *print_object : m_objects)
     for (const PrintObject *print_object : m_objects)
-        instances += print_object->copies().size();
+        instances += (unsigned int)print_object->copies().size();
     return instances;
     return instances;
 }
 }
 
 
@@ -360,7 +330,7 @@ double Print::max_allowed_layer_height() const
 
 
 // Caller is responsible for supplying models whose objects don't collide
 // Caller is responsible for supplying models whose objects don't collide
 // and have explicit instance positions.
 // and have explicit instance positions.
-void Print::add_model_object(ModelObject* model_object, int idx)
+void Print::add_model_object_perl_tests_only(ModelObject* model_object, int idx)
 {
 {
 	tbb::mutex::scoped_lock lock(this->state_mutex());
 	tbb::mutex::scoped_lock lock(this->state_mutex());
     // Add a copy of this ModelObject to this Print.
     // Add a copy of this ModelObject to this Print.
@@ -389,26 +359,26 @@ void Print::add_model_object(ModelObject* model_object, int idx)
 		object->set_trafo(trafo);
 		object->set_trafo(trafo);
     }
     }
 
 
-    size_t volume_id = 0;
+    int volume_id = 0;
     for (const ModelVolume *volume : model_object->volumes) {
     for (const ModelVolume *volume : model_object->volumes) {
         if (! volume->is_model_part() && ! volume->is_modifier())
         if (! volume->is_model_part() && ! volume->is_modifier())
             continue;
             continue;
         // Get the config applied to this volume.
         // Get the config applied to this volume.
-        PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999);
+        PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, nullptr, *volume, 99999);
         // Find an existing print region with the same config.
         // Find an existing print region with the same config.
-        size_t region_id = size_t(-1);
-        for (size_t i = 0; i < m_regions.size(); ++ i)
+        int region_id = -1;
+        for (int i = 0; i < (int)m_regions.size(); ++ i)
             if (config.equals(m_regions[i]->config())) {
             if (config.equals(m_regions[i]->config())) {
                 region_id = i;
                 region_id = i;
                 break;
                 break;
             }
             }
         // If no region exists with the same config, create a new one.
         // If no region exists with the same config, create a new one.
-        if (region_id == size_t(-1)) {
-            region_id = m_regions.size();
+        if (region_id == -1) {
+            region_id = (int)m_regions.size();
             this->add_region(config);
             this->add_region(config);
         }
         }
         // Assign volume to a region.
         // Assign volume to a region.
-        object->add_region_volume(region_id, volume_id);
+        object->add_region_volume((unsigned int)region_id, volume_id, t_layer_height_range(0, DBL_MAX));
         ++ volume_id;
         ++ volume_id;
     }
     }
 
 
@@ -489,18 +459,18 @@ bool Print::apply_config_perl_tests_only(DynamicPrintConfig config)
             bool               this_region_config_set = false;
             bool               this_region_config_set = false;
             for (PrintObject *object : m_objects) {
             for (PrintObject *object : m_objects) {
                 if (region_id < object->region_volumes.size()) {
                 if (region_id < object->region_volumes.size()) {
-                    for (int volume_id : object->region_volumes[region_id]) {
-                        const ModelVolume &volume = *object->model_object()->volumes[volume_id];
+                    for (const std::pair<t_layer_height_range, int> &volume_and_range : object->region_volumes[region_id]) {
+                        const ModelVolume &volume = *object->model_object()->volumes[volume_and_range.second];
                         if (this_region_config_set) {
                         if (this_region_config_set) {
                             // If the new config for this volume differs from the other
                             // If the new config for this volume differs from the other
                             // volume configs currently associated to this region, it means
                             // volume configs currently associated to this region, it means
                             // the region subdivision does not make sense anymore.
                             // the region subdivision does not make sense anymore.
-                            if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
+                            if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, nullptr, volume, 99999))) {
                                 rearrange_regions = true;
                                 rearrange_regions = true;
                                 goto exit_for_rearrange_regions;
                                 goto exit_for_rearrange_regions;
                             }
                             }
                         } else {
                         } else {
-                            this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
+                            this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, nullptr, volume, 99999);
                             this_region_config_set = true;
                             this_region_config_set = true;
                         }
                         }
                         for (const PrintRegionConfig &cfg : other_region_configs) {
                         for (const PrintRegionConfig &cfg : other_region_configs) {
@@ -540,7 +510,7 @@ exit_for_rearrange_regions:
             model_objects.push_back(object->model_object());
             model_objects.push_back(object->model_object());
         this->clear();
         this->clear();
         for (ModelObject *mo : model_objects)
         for (ModelObject *mo : model_objects)
-            this->add_model_object(mo);
+            this->add_model_object_perl_tests_only(mo);
         invalidated = true;
         invalidated = true;
     }
     }
 
 
@@ -620,6 +590,20 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
     }
     }
 }
 }
 
 
+static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
+{
+    assert(lr_dst.size() == lr_src.size());
+    auto it_src = lr_src.cbegin();
+    for (auto &kvp_dst : lr_dst) {
+        const auto &kvp_src = *it_src ++;
+        assert(std::abs(kvp_dst.first.first  - kvp_src.first.first ) <= EPSILON);
+        assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
+        // Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
+        // assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
+        kvp_dst.second = kvp_src.second;
+    }
+}
+
 static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs) 
 static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs) 
 {
 {
     typedef Transform3d::Scalar T;
     typedef Transform3d::Scalar T;
@@ -674,6 +658,23 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
     return std::vector<PrintInstances>(trafos.begin(), trafos.end());
     return std::vector<PrintInstances>(trafos.begin(), trafos.end());
 }
 }
 
 
+// Compare just the layer ranges and their layer heights, not the associated configs.
+// Ignore the layer heights if check_layer_heights is false.
+bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
+{
+    if (lr1.size() != lr2.size())
+        return false;
+    auto it2 = lr2.begin();
+    for (const auto &kvp1 : lr1) {
+        const auto &kvp2 = *it2 ++;
+        if (std::abs(kvp1.first.first  - kvp2.first.first ) > EPSILON ||
+            std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
+            (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
+            return false;
+    }
+    return true;
+}
+
 Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
 Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
 {
 {
 #ifdef _DEBUG
 #ifdef _DEBUG
@@ -724,6 +725,50 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
     // Handle changes to regions config defaults
     // Handle changes to regions config defaults
     m_default_region_config.apply_only(config, region_diff, true);
     m_default_region_config.apply_only(config, region_diff, true);
     
     
+    class LayerRanges
+    {
+    public:
+        LayerRanges() {}
+        // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
+        void assign(const t_layer_config_ranges &in) {
+            m_ranges.clear();
+            m_ranges.reserve(in.size());
+            // Input ranges are sorted lexicographically. First range trims the other ranges.
+            coordf_t last_z = 0;
+            for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in) {
+//            for (auto &range : in) {
+			if (range.first.second > last_z) {
+                    coordf_t min_z = std::max(range.first.first, 0.);
+                    if (min_z > last_z + EPSILON) {
+                        m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
+                        last_z = min_z;
+                    }
+                    if (range.first.second > last_z + EPSILON) {
+						const DynamicPrintConfig* cfg = &range.second;
+                        m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
+                        last_z = range.first.second;
+                    }
+                }
+            }
+            if (m_ranges.empty())
+                m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
+            else if (m_ranges.back().second == nullptr)
+                m_ranges.back().first.second = DBL_MAX;
+            else
+                m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
+        }
+        const DynamicPrintConfig* config(const t_layer_height_range &range) const {
+            auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
+            assert(it != m_ranges.end());
+            assert(it == m_ranges.end() || std::abs(it->first.first  - range.first ) < EPSILON);
+            assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
+            return (it == m_ranges.end()) ? nullptr : it->second;
+        }
+        auto begin() const { return m_ranges.cbegin(); }
+        auto end() const { return m_ranges.cend(); }
+    private:
+        std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
+    };
     struct ModelObjectStatus {
     struct ModelObjectStatus {
         enum Status {
         enum Status {
             Unknown,
             Unknown,
@@ -733,8 +778,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
             Deleted,
             Deleted,
         };
         };
         ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
         ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
-        ModelID                 id;
-        Status                  status;
+        ModelID     id;
+        Status      status;
+        LayerRanges layer_ranges;
         // Search by id.
         // Search by id.
         bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
         bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
     };
     };
@@ -861,22 +907,23 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
         auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
         auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
         assert(it_status != model_object_status.end());
         assert(it_status != model_object_status.end());
         assert(it_status->status != ModelObjectStatus::Deleted);
         assert(it_status->status != ModelObjectStatus::Deleted);
+		const ModelObject& model_object_new = *model.objects[idx_model_object];
+		const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
         if (it_status->status == ModelObjectStatus::New)
         if (it_status->status == ModelObjectStatus::New)
             // PrintObject instances will be added in the next loop.
             // PrintObject instances will be added in the next loop.
             continue;
             continue;
         // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
         // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
         assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
         assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
-        const ModelObject &model_object_new = *model.objects[idx_model_object];
         // Check whether a model part volume was added or removed, their transformations or order changed.
         // Check whether a model part volume was added or removed, their transformations or order changed.
+        // Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked.
         bool model_parts_differ         = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
         bool model_parts_differ         = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
         bool modifiers_differ           = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
         bool modifiers_differ           = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
         bool support_blockers_differ    = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
         bool support_blockers_differ    = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
         bool support_enforcers_differ   = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
         bool support_enforcers_differ   = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
         if (model_parts_differ || modifiers_differ || 
         if (model_parts_differ || modifiers_differ || 
             model_object.origin_translation         != model_object_new.origin_translation   ||
             model_object.origin_translation         != model_object_new.origin_translation   ||
-//             model_object.layer_height_ranges        != model_object_new.layer_height_ranges  || 
-            model_object.layer_config_ranges        != model_object_new.layer_config_ranges  ||         // #ys_FIXME_experiment
-            model_object.layer_height_profile       != model_object_new.layer_height_profile) {
+            model_object.layer_height_profile       != model_object_new.layer_height_profile ||
+            ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
             // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
             // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
             auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
             auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
             for (auto it = range.first; it != range.second; ++ it) {
             for (auto it = range.first; it != range.second; ++ it) {
@@ -916,7 +963,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
             //FIXME What to do with m_material_id?
             //FIXME What to do with m_material_id?
 			model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
 			model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
 			model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
 			model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
-            // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
+            layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
+            // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
             model_object.name       = model_object_new.name;
             model_object.name       = model_object_new.name;
             model_object.input_file = model_object_new.input_file;
             model_object.input_file = model_object_new.input_file;
             model_object.clear_instances();
             model_object.clear_instances();
@@ -1028,19 +1076,27 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
         PrintRegionConfig  this_region_config;
         PrintRegionConfig  this_region_config;
         bool               this_region_config_set = false;
         bool               this_region_config_set = false;
         for (PrintObject *print_object : m_objects) {
         for (PrintObject *print_object : m_objects) {
+            const LayerRanges *layer_ranges;
+            {
+                auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
+                assert(it_status != model_object_status.end());
+                assert(it_status->status != ModelObjectStatus::Deleted);
+                layer_ranges = &it_status->layer_ranges;
+            }
             if (region_id < print_object->region_volumes.size()) {
             if (region_id < print_object->region_volumes.size()) {
-                for (int volume_id : print_object->region_volumes[region_id]) {
-                    const ModelVolume &volume = *print_object->model_object()->volumes[volume_id];
+                for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
+                    const ModelVolume        &volume             = *print_object->model_object()->volumes[volume_and_range.second];
+                    const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
                     if (this_region_config_set) {
                     if (this_region_config_set) {
                         // If the new config for this volume differs from the other
                         // If the new config for this volume differs from the other
                         // volume configs currently associated to this region, it means
                         // volume configs currently associated to this region, it means
                         // the region subdivision does not make sense anymore.
                         // the region subdivision does not make sense anymore.
-                        if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
+                        if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
                             // Regions were split. Reset this print_object.
                             // Regions were split. Reset this print_object.
                             goto print_object_end;
                             goto print_object_end;
                     } else {
                     } else {
-                        this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders);
-						for (size_t i = 0; i < region_id; ++i) {
+                        this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
+						for (size_t i = 0; i < region_id; ++ i) {
 							const PrintRegion &region_other = *m_regions[i];
 							const PrintRegion &region_other = *m_regions[i];
 							if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
 							if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
 								// Regions were merged. Reset this print_object.
 								// Regions were merged. Reset this print_object.
@@ -1055,7 +1111,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
             update_apply_status(print_object->invalidate_all_steps());
             update_apply_status(print_object->invalidate_all_steps());
             // Decrease the references to regions from this volume.
             // Decrease the references to regions from this volume.
             int ireg = 0;
             int ireg = 0;
-            for (const std::vector<int> &volumes : print_object->region_volumes) {
+            for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
                 if (! volumes.empty())
                 if (! volumes.empty())
                     -- m_regions[ireg]->m_refcnt;
                     -- m_regions[ireg]->m_refcnt;
                 ++ ireg;
                 ++ ireg;
@@ -1077,52 +1133,65 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
     for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
     for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
         PrintObject        &print_object0 = *m_objects[idx_print_object];
         PrintObject        &print_object0 = *m_objects[idx_print_object];
         const ModelObject  &model_object  = *print_object0.model_object();
         const ModelObject  &model_object  = *print_object0.model_object();
-        std::vector<int>    map_volume_to_region(model_object.volumes.size(), -1);
+        const LayerRanges *layer_ranges;
+        {
+            auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
+            assert(it_status != model_object_status.end());
+            assert(it_status->status != ModelObjectStatus::Deleted);
+            layer_ranges = &it_status->layer_ranges;
+        }
+        std::vector<int>   regions_in_object;
+        regions_in_object.reserve(64);
         for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
         for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
             PrintObject &print_object = *m_objects[i];
             PrintObject &print_object = *m_objects[i];
 			bool         fresh = print_object.region_volumes.empty();
 			bool         fresh = print_object.region_volumes.empty();
             unsigned int volume_id = 0;
             unsigned int volume_id = 0;
+            unsigned int idx_region_in_object = 0;
             for (const ModelVolume *volume : model_object.volumes) {
             for (const ModelVolume *volume : model_object.volumes) {
                 if (! volume->is_model_part() && ! volume->is_modifier()) {
                 if (! volume->is_model_part() && ! volume->is_modifier()) {
 					++ volume_id;
 					++ volume_id;
 					continue;
 					continue;
 				}
 				}
-                int region_id = -1;
-                if (&print_object == &print_object0) {
-                    // Get the config applied to this volume.
-                    PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
-                    // Find an existing print region with the same config.
-					int idx_empty_slot = -1;
-					for (int i = 0; i < (int)m_regions.size(); ++ i) {
-						if (m_regions[i]->m_refcnt == 0) {
-                            if (idx_empty_slot == -1)
-                                idx_empty_slot = i;
-                        } else if (config.equals(m_regions[i]->config())) {
-                            region_id = i;
-                            break;
+                // Filter the layer ranges, so they do not overlap and they contain at least a single layer.
+                // Now insert a volume with a layer range to its own region.
+                for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
+                    int region_id = -1;
+                    if (&print_object == &print_object0) {
+                        // Get the config applied to this volume.
+                        PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
+                        // Find an existing print region with the same config.
+    					int idx_empty_slot = -1;
+    					for (int i = 0; i < (int)m_regions.size(); ++ i) {
+    						if (m_regions[i]->m_refcnt == 0) {
+                                if (idx_empty_slot == -1)
+                                    idx_empty_slot = i;
+                            } else if (config.equals(m_regions[i]->config())) {
+                                region_id = i;
+                                break;
+                            }
+    					}
+                        // If no region exists with the same config, create a new one.
+    					if (region_id == -1) {
+    						if (idx_empty_slot == -1) {
+    							region_id = (int)m_regions.size();
+    							this->add_region(config);
+    						} else {
+    							region_id = idx_empty_slot;
+                                m_regions[region_id]->set_config(std::move(config));
+    						}
                         }
                         }
-					}
-                    // If no region exists with the same config, create a new one.
-					if (region_id == -1) {
-						if (idx_empty_slot == -1) {
-							region_id = (int)m_regions.size();
-							this->add_region(config);
-						} else {
-							region_id = idx_empty_slot;
-                            m_regions[region_id]->set_config(std::move(config));
-						}
-                    }
-                    map_volume_to_region[volume_id] = region_id;
-                } else
-                    region_id = map_volume_to_region[volume_id];
-                // Assign volume to a region.
-				if (fresh) {
-					if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
-						++ m_regions[region_id]->m_refcnt;
-					print_object.add_region_volume(region_id, volume_id);
-				}
-                ++ volume_id;
-            }
+                        regions_in_object.emplace_back(region_id);
+                    } else
+                        region_id = regions_in_object[idx_region_in_object ++];
+                    // Assign volume to a region.
+    				if (fresh) {
+    					if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
+    						++ m_regions[region_id]->m_refcnt;
+    					print_object.add_region_volume(region_id, volume_id, it_range->first);
+    				}
+                }
+				++ volume_id;
+			}
         }
         }
     }
     }
 
 
@@ -1176,7 +1245,7 @@ std::string Print::validate() const
                 Polygon        convex_hull0    = offset(
                 Polygon        convex_hull0    = offset(
                     print_object->model_object()->convex_hull_2d(
                     print_object->model_object()->convex_hull_2d(
                         Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
                         Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
-                    scale_(m_config.extruder_clearance_radius.value) / 2., jtRound, scale_(0.1)).front();
+                    float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
                 // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
                 // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
                 for (const Point &copy : print_object->m_copies) {
                 for (const Point &copy : print_object->m_copies) {
                     Polygon convex_hull = convex_hull0;
                     Polygon convex_hull = convex_hull0;
@@ -1228,7 +1297,6 @@ std::string Print::validate() const
             bool                                has_custom_layering = false;
             bool                                has_custom_layering = false;
             std::vector<std::vector<coordf_t>>  layer_height_profiles;
             std::vector<std::vector<coordf_t>>  layer_height_profiles;
             for (const PrintObject *object : m_objects) {
             for (const PrintObject *object : m_objects) {
-//                 has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
                 has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty();      // #ys_FIXME_experiment
                 has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty();      // #ys_FIXME_experiment
                 if (has_custom_layering) {
                 if (has_custom_layering) {
                     layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
                     layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
@@ -1437,9 +1505,9 @@ Flow Print::brim_flow() const
        generation as well. */
        generation as well. */
     return Flow::new_from_config_width(
     return Flow::new_from_config_width(
         frPerimeter,
         frPerimeter,
-        width, 
-        m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
-        this->skirt_first_layer_height(),
+		width,
+        (float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
+		(float)this->skirt_first_layer_height(),
         0
         0
     );
     );
 }
 }
@@ -1459,9 +1527,9 @@ Flow Print::skirt_flow() const
        generation as well. */
        generation as well. */
     return Flow::new_from_config_width(
     return Flow::new_from_config_width(
         frPerimeter,
         frPerimeter,
-        width, 
-        m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
-        this->skirt_first_layer_height(),
+		width,
+		(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
+		(float)this->skirt_first_layer_height(),
         0
         0
     );
     );
 }
 }
@@ -1636,20 +1704,20 @@ void Print::_make_skirt()
 
 
     // Initial offset of the brim inner edge from the object (possible with a support & raft).
     // Initial offset of the brim inner edge from the object (possible with a support & raft).
     // The skirt will touch the brim if the brim is extruded.
     // The skirt will touch the brim if the brim is extruded.
-    Flow brim_flow = this->brim_flow();
+    Flow   brim_flow = this->brim_flow();
     double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
     double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
-    coord_t distance = scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.);
+    auto   distance = float(scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.));
     // Draw outlines from outside to inside.
     // Draw outlines from outside to inside.
     // Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
     // Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
     std::vector<coordf_t> extruded_length(extruders.size(), 0.);
     std::vector<coordf_t> extruded_length(extruders.size(), 0.);
     for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
     for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
         this->throw_if_canceled();
         this->throw_if_canceled();
         // Offset the skirt outside.
         // Offset the skirt outside.
-        distance += coord_t(scale_(spacing));
+        distance += float(scale_(spacing));
         // Generate the skirt centerline.
         // Generate the skirt centerline.
         Polygon loop;
         Polygon loop;
         {
         {
-            Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1));
+            Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
             Geometry::simplify_polygons(loops, scale_(0.05), &loops);
             Geometry::simplify_polygons(loops, scale_(0.05), &loops);
 			if (loops.empty())
 			if (loops.empty())
 				break;
 				break;
@@ -1660,9 +1728,9 @@ void Print::_make_skirt()
         eloop.paths.emplace_back(ExtrusionPath(
         eloop.paths.emplace_back(ExtrusionPath(
             ExtrusionPath(
             ExtrusionPath(
                 erSkirt,
                 erSkirt,
-                mm3_per_mm,         // this will be overridden at G-code export time
+                (float)mm3_per_mm,         // this will be overridden at G-code export time
                 flow.width,
                 flow.width,
-                first_layer_height  // this will be overridden at G-code export time
+				(float)first_layer_height  // this will be overridden at G-code export time
             )));
             )));
         eloop.paths.back().polyline = loop.split_at_first_point();
         eloop.paths.back().polyline = loop.split_at_first_point();
         m_skirt.append(eloop);
         m_skirt.append(eloop);
@@ -1788,7 +1856,7 @@ void Print::_make_wipe_tower()
                 // Insert the new support layer.
                 // Insert the new support layer.
                 double height    = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z;
                 double height    = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z;
                 //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
                 //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
-                it_layer = m_objects.front()->insert_support_layer(it_layer, size_t(-1), height, lt.print_z, lt.print_z - 0.5 * height);
+                it_layer = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height);
                 ++ it_layer;
                 ++ it_layer;
             }
             }
         }
         }
@@ -1815,19 +1883,19 @@ void Print::_make_wipe_tower()
             WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
             WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
             m_config.temperature.get_at(i),
             m_config.temperature.get_at(i),
             m_config.first_layer_temperature.get_at(i),
             m_config.first_layer_temperature.get_at(i),
-            m_config.filament_loading_speed.get_at(i),
-            m_config.filament_loading_speed_start.get_at(i),
-            m_config.filament_unloading_speed.get_at(i),
-            m_config.filament_unloading_speed_start.get_at(i),
-            m_config.filament_toolchange_delay.get_at(i),
+			(float)m_config.filament_loading_speed.get_at(i),
+			(float)m_config.filament_loading_speed_start.get_at(i),
+			(float)m_config.filament_unloading_speed.get_at(i),
+			(float)m_config.filament_unloading_speed_start.get_at(i),
+			(float)m_config.filament_toolchange_delay.get_at(i),
             m_config.filament_cooling_moves.get_at(i),
             m_config.filament_cooling_moves.get_at(i),
-            m_config.filament_cooling_initial_speed.get_at(i),
-            m_config.filament_cooling_final_speed.get_at(i),
+			(float)m_config.filament_cooling_initial_speed.get_at(i),
+			(float)m_config.filament_cooling_final_speed.get_at(i),
             m_config.filament_ramming_parameters.get_at(i),
             m_config.filament_ramming_parameters.get_at(i),
-            m_config.nozzle_diameter.get_at(i));
+			(float)m_config.nozzle_diameter.get_at(i));
 
 
     m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
     m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
-        wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
+        wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
 
 
     // Lets go through the wipe tower layers and determine pairs of extruder changes for each
     // Lets go through the wipe tower layers and determine pairs of extruder changes for each
     // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
     // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
@@ -1836,21 +1904,21 @@ void Print::_make_wipe_tower()
         for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
         for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
             if (!layer_tools.has_wipe_tower) continue;
             if (!layer_tools.has_wipe_tower) continue;
             bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
             bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
-            wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
+            wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id, false);
             for (const auto extruder_id : layer_tools.extruders) {
             for (const auto extruder_id : layer_tools.extruders) {
                 if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
                 if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];             // total volume to wipe after this toolchange
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];             // total volume to wipe after this toolchange
                     // Not all of that can be used for infill purging:
                     // Not all of that can be used for infill purging:
-                    volume_to_wipe -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+                    volume_to_wipe -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
 
 
                     // try to assign some infills/objects for the wiping:
                     // try to assign some infills/objects for the wiping:
                     volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
                     volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
 
 
                     // add back the minimal amount toforce on the wipe tower:
                     // add back the minimal amount toforce on the wipe tower:
-                    volume_to_wipe += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+                    volume_to_wipe += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
 
 
                     // request a toolchange at the wipe tower with at least volume_to_wipe purging amount
                     // request a toolchange at the wipe tower with at least volume_to_wipe purging amount
-                    wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
+                    wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
                                                first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
                                                first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
                     current_extruder_id = extruder_id;
                     current_extruder_id = extruder_id;
                 }
                 }

+ 14 - 12
src/libslic3r/Print.hpp

@@ -80,8 +80,8 @@ private: // Prevents erroneous use by other classes.
     typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
     typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
 
 
 public:
 public:
-    // vector of (vectors of volume ids), indexed by region_id
-    std::vector<std::vector<int>> region_volumes;
+    // vector of (layer height ranges and vectors of volume ids), indexed by region_id
+    std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
 
 
     // this is set to true when LayerRegion->slices is split in top/internal/bottom
     // this is set to true when LayerRegion->slices is split in top/internal/bottom
     // so that next call to make_perimeters() performs a union() before computing loops
     // so that next call to make_perimeters() performs a union() before computing loops
@@ -99,10 +99,10 @@ public:
     BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
     BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
 
 
     // adds region_id, too, if necessary
     // adds region_id, too, if necessary
-    void add_region_volume(unsigned int region_id, int volume_id) {
+    void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
         if (region_id >= region_volumes.size())
         if (region_id >= region_volumes.size())
 			region_volumes.resize(region_id + 1);
 			region_volumes.resize(region_id + 1);
-        region_volumes[region_id].emplace_back(volume_id);
+        region_volumes[region_id].emplace_back(layer_range, volume_id);
     }
     }
     // This is the *total* layer count (including support layers)
     // This is the *total* layer count (including support layers)
     // this value is not supposed to be compared with Layer::id
     // this value is not supposed to be compared with Layer::id
@@ -141,8 +141,9 @@ public:
     void slice();
     void slice();
 
 
     // Helpers to slice support enforcer / blocker meshes by the support generator.
     // 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;
+    std::vector<ExPolygons>     slice_support_volumes(const ModelVolumeType &model_volume_type) const;
+    std::vector<ExPolygons>     slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
+    std::vector<ExPolygons>     slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
 
 
 protected:
 protected:
     // to be called from Print only.
     // to be called from Print only.
@@ -165,7 +166,7 @@ protected:
     void                    update_slicing_parameters();
     void                    update_slicing_parameters();
 
 
     static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
     static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
-    static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
+    static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
 
 
 private:
 private:
     void make_perimeters();
     void make_perimeters();
@@ -201,9 +202,11 @@ private:
     LayerPtrs                               m_layers;
     LayerPtrs                               m_layers;
     SupportLayerPtrs                        m_support_layers;
     SupportLayerPtrs                        m_support_layers;
 
 
-    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;
-    std::vector<ExPolygons> _slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
+    std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z) const;
+    std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
+    std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
+    std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
+    std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const;
 };
 };
 
 
 struct WipeTowerData
 struct WipeTowerData
@@ -292,8 +295,7 @@ public:
     ApplyStatus         apply(const Model &model, const DynamicPrintConfig &config) override;
     ApplyStatus         apply(const Model &model, const DynamicPrintConfig &config) override;
 
 
     // The following three methods are used by the Perl tests only. Get rid of them!
     // The following three methods are used by the Perl tests only. Get rid of them!
-    void                reload_object(size_t idx);
-    void                add_model_object(ModelObject* model_object, int idx = -1);
+    void                add_model_object_perl_tests_only(ModelObject* model_object, int idx = -1);
     bool                apply_config_perl_tests_only(DynamicPrintConfig config);
     bool                apply_config_perl_tests_only(DynamicPrintConfig config);
 
 
     void                process() override;
     void                process() override;

+ 243 - 90
src/libslic3r/PrintObject.cpp

@@ -49,7 +49,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
     {
     {
         // Translate meshes so that our toolpath generation algorithms work with smaller
         // Translate meshes so that our toolpath generation algorithms work with smaller
         // XY coordinates; this translation is an optimization and not strictly required.
         // XY coordinates; this translation is an optimization and not strictly required.
-        // A cloned mesh will be aligned to 0 before slicing in _slice_region() since we
+        // A cloned mesh will be aligned to 0 before slicing in slice_region() since we
         // don't assume it's already aligned and we don't alter the original position in model.
         // don't assume it's already aligned and we don't alter the original position in model.
         // We store the XY translation so that we can place copies correctly in the output G-code
         // We store the XY translation so that we can place copies correctly in the output G-code
         // (copies are expressed in G-code coordinates and this translation is not publicly exposed).
         // (copies are expressed in G-code coordinates and this translation is not publicly exposed).
@@ -590,7 +590,12 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
 
 
 bool PrintObject::invalidate_all_steps()
 bool PrintObject::invalidate_all_steps()
 {
 {
-    return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
+	// First call the "invalidate" functions, which may cancel background processing.
+    bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
+	// Then reset some of the depending values.
+	this->m_slicing_params.valid = false;
+	this->region_volumes.clear();
+	return result;
 }
 }
 
 
 bool PrintObject::has_support_material() const
 bool PrintObject::has_support_material() const
@@ -1354,10 +1359,12 @@ PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObject
     return config;
     return config;
 }
 }
 
 
-PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
+PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
 {
 {
     PrintRegionConfig config = default_region_config;
     PrintRegionConfig config = default_region_config;
     normalize_and_apply_config(config, volume.get_object()->config);
     normalize_and_apply_config(config, volume.get_object()->config);
+    if (layer_range_config != nullptr)
+    	normalize_and_apply_config(config, *layer_range_config);
     normalize_and_apply_config(config, volume.config);
     normalize_and_apply_config(config, volume.config);
     if (! volume.material_id().empty())
     if (! volume.material_id().empty())
         normalize_and_apply_config(config, volume.material()->config);
         normalize_and_apply_config(config, volume.material()->config);
@@ -1375,28 +1382,37 @@ void PrintObject::update_slicing_parameters()
             this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
             this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
 }
 }
 
 
-SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z)
+SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
 {
 {
-    PrintConfig         print_config;
-    PrintObjectConfig   object_config;
-    PrintRegionConfig   default_region_config;
-    print_config .apply(full_config, true);
-    object_config.apply(full_config, true);
-    default_region_config.apply(full_config, true);
-    size_t              num_extruders = print_config.nozzle_diameter.size();
-    object_config = object_config_from_model_object(object_config, model_object, num_extruders);
-
-    std::vector<unsigned int> object_extruders;
-    for (const ModelVolume *model_volume : model_object.volumes)
-        if (model_volume->is_model_part())
-            PrintRegion::collect_object_printing_extruders(
-                print_config,
-                region_config_from_model_volume(default_region_config, *model_volume, num_extruders),
-                object_extruders);
+	PrintConfig         print_config;
+	PrintObjectConfig   object_config;
+	PrintRegionConfig   default_region_config;
+	print_config.apply(full_config, true);
+	object_config.apply(full_config, true);
+	default_region_config.apply(full_config, true);
+	size_t              num_extruders = print_config.nozzle_diameter.size();
+	object_config = object_config_from_model_object(object_config, model_object, num_extruders);
+
+	std::vector<unsigned int> object_extruders;
+	for (const ModelVolume* model_volume : model_object.volumes)
+		if (model_volume->is_model_part()) {
+			PrintRegion::collect_object_printing_extruders(
+				print_config,
+				region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders),
+				object_extruders);
+			for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range_and_config : model_object.layer_config_ranges)
+				if (range_and_config.second.has("perimeter_extruder") ||
+					range_and_config.second.has("infill_extruder") ||
+					range_and_config.second.has("solid_infill_extruder"))
+					PrintRegion::collect_object_printing_extruders(
+						print_config,
+						region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders),
+						object_extruders);
+		}
     sort_remove_duplicates(object_extruders);
     sort_remove_duplicates(object_extruders);
 
 
     if (object_max_z <= 0.f)
     if (object_max_z <= 0.f)
-        object_max_z = model_object.raw_bounding_box().size().z();
+        object_max_z = (float)model_object.raw_bounding_box().size().z();
 	return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
 	return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
 }
 }
 
 
@@ -1430,13 +1446,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
         layer_height_profile.clear();
         layer_height_profile.clear();
 
 
     if (layer_height_profile.empty()) {
     if (layer_height_profile.empty()) {
-        if (0)
+    	if (0)
 //        if (this->layer_height_profile.empty())
 //        if (this->layer_height_profile.empty())
-            layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes);
+        	layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
         else
         else
-//             layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges);
-             layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);     // #ys_FIXME_experiment
-       updated = true;
+        	layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);     // #ys_FIXME_experiment
+       	updated = true;
     }
     }
     return updated;
     return updated;
 }
 }
@@ -1490,22 +1505,28 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
     }
     }
 
 
     // Count model parts and modifier meshes, check whether the model parts are of the same region.
     // Count model parts and modifier meshes, check whether the model parts are of the same region.
-    int              single_volume_region = -2; // not set yet
+    int              all_volumes_single_region = -2; // not set yet
+    bool 			 has_z_ranges  = false;
 	size_t           num_volumes   = 0;
 	size_t           num_volumes   = 0;
     size_t           num_modifiers = 0;
     size_t           num_modifiers = 0;
-    std::vector<int> map_volume_to_region(this->model_object()->volumes.size());
     for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
     for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
-        for (int volume_id : this->region_volumes[region_id]) {
+		int last_volume_id = -1;
+        for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
+			const int		   volume_id    = volume_and_range.second;
 			const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
 			const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
             if (model_volume->is_model_part()) {
             if (model_volume->is_model_part()) {
-                map_volume_to_region[volume_id] = region_id;
-                if (single_volume_region == -2)
-                    // first model volume met
-                    single_volume_region = region_id;
-                else if (single_volume_region != region_id)
-                    // multiple volumes met and they are not equal
-                    single_volume_region = -1;
-				++ num_volumes;
+				if (last_volume_id == volume_id) {
+					has_z_ranges = true;
+				} else {
+					last_volume_id = volume_id;
+					if (all_volumes_single_region == -2)
+						// first model volume met
+						all_volumes_single_region = region_id;
+					else if (all_volumes_single_region != region_id)
+						// multiple volumes met and they are not equal
+						all_volumes_single_region = -1;
+					++ num_volumes;
+				}
             } else if (model_volume->is_modifier())
             } else if (model_volume->is_modifier())
                 ++ num_modifiers;
                 ++ num_modifiers;
         }
         }
@@ -1515,13 +1536,13 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
     // Slice all non-modifier volumes.
     // Slice all non-modifier volumes.
     bool clipped  = false;
     bool clipped  = false;
     bool upscaled = false;
     bool upscaled = false;
-    if (! m_config.clip_multipart_objects.value || single_volume_region >= 0) {
+    if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
         // Cheap path: Slice regions without mutual clipping.
         // Cheap path: Slice regions without mutual clipping.
         // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
         // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
         for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
         for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
             BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
             BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
             // slicing in parallel
             // slicing in parallel
-            std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
+            std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs);
             m_print->throw_if_canceled();
             m_print->throw_if_canceled();
             BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
             BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
             for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
             for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@@ -1542,15 +1563,29 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
         };
         };
         std::vector<SlicedVolume> sliced_volumes;
         std::vector<SlicedVolume> sliced_volumes;
         sliced_volumes.reserve(num_volumes);
         sliced_volumes.reserve(num_volumes);
-		for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
-			for (int volume_id : this->region_volumes[region_id]) {
+		for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
+			const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
+			for (size_t i = 0; i < volumes_and_ranges.size(); ) {
+				int 			   volume_id    = volumes_and_ranges[i].second;
 				const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
 				const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
 				if (model_volume->is_model_part()) {
 				if (model_volume->is_model_part()) {
 					BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
 					BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
+					// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
+					std::vector<t_layer_height_range> ranges;
+					ranges.emplace_back(volumes_and_ranges[i].first);
+					size_t j = i + 1;
+					for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
+						if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
+							ranges.back().second = volumes_and_ranges[j].first.second;
+						else
+							ranges.emplace_back(volumes_and_ranges[j].first);
                     // slicing in parallel
                     // slicing in parallel
-					sliced_volumes.emplace_back(volume_id, map_volume_to_region[volume_id], this->_slice_volume(slice_zs, *model_volume));
-				}
+					sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume));
+					i = j;
+				} else
+					++ i;
 			}
 			}
+		}
         // Second clip the volumes in the order they are presented at the user interface.
         // Second clip the volumes in the order they are presented at the user interface.
         BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
         BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
         tbb::parallel_for(
         tbb::parallel_for(
@@ -1604,7 +1639,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
         for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
         for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
             BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
             BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
             // slicing in parallel
             // slicing in parallel
-            std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
+            std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
             m_print->throw_if_canceled();
             m_print->throw_if_canceled();
             if (expolygons_by_layer.empty())
             if (expolygons_by_layer.empty())
                 continue;
                 continue;
@@ -1620,7 +1655,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
                             Layer       *layer = m_layers[layer_id];
                             Layer       *layer = m_layers[layer_id];
                             LayerRegion *layerm = layer->m_regions[region_id];
                             LayerRegion *layerm = layer->m_regions[region_id];
                             LayerRegion *other_layerm = layer->m_regions[other_region_id];
                             LayerRegion *other_layerm = layer->m_regions[other_region_id];
-                            if (layerm == nullptr || other_layerm == nullptr)
+                            if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
                                 continue;
                                 continue;
                             Polygons other_slices = to_polygons(other_layerm->slices);
                             Polygons other_slices = to_polygons(other_layerm->slices);
                             ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
                             ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
@@ -1753,46 +1788,127 @@ end:
     BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
     BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
 }
 }
 
 
-std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
+// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
+std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z) const
 {
 {
-    std::vector<const ModelVolume*> volumes;
+	std::vector<const ModelVolume*> volumes;
     if (region_id < this->region_volumes.size()) {
     if (region_id < this->region_volumes.size()) {
-        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);
-        }
+		for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
+			const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
+			if (volume->is_model_part())
+				volumes.emplace_back(volume);
+		}
     }
     }
-    return this->_slice_volumes(z, volumes);
+	return this->slice_volumes(z, volumes);
 }
 }
 
 
-std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+// Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once.
+std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) 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((float)l->slice_z);
-    return this->_slice_volumes(zs, volumes);
+	std::vector<ExPolygons> out;
+    if (region_id < this->region_volumes.size())
+    {
+		std::vector<std::vector<t_layer_height_range>> volume_ranges;
+		const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
+		volume_ranges.reserve(volumes_and_ranges.size());
+		for (size_t i = 0; i < volumes_and_ranges.size(); ) {
+			int 			   volume_id    = volumes_and_ranges[i].second;
+			const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
+			if (model_volume->is_modifier()) {
+				std::vector<t_layer_height_range> ranges;
+				ranges.emplace_back(volumes_and_ranges[i].first);
+				size_t j = i + 1;
+				for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) {
+					if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
+						ranges.back().second = volumes_and_ranges[j].first.second;
+					else
+						ranges.emplace_back(volumes_and_ranges[j].first);
+				}
+				volume_ranges.emplace_back(std::move(ranges));
+				i = j;
+			} else
+				++ i;
+		}
+
+		if (! volume_ranges.empty()) 
+		{
+			bool equal_ranges = true;
+			for (size_t i = 1; i < volume_ranges.size(); ++ i) {
+				assert(! volume_ranges[i].empty());
+				if (volume_ranges.front() != volume_ranges[i]) {
+					equal_ranges = false;
+					break;
+				}
+			}
+
+			if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
+				// No modifier in this region was split to layer spans.
+				std::vector<const ModelVolume*> volumes;
+				for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
+					const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
+					if (volume->is_modifier())
+						volumes.emplace_back(volume);
+				}
+				out = this->slice_volumes(slice_zs, volumes);
+			} else {
+				// Some modifier in this region was split to layer spans.
+				std::vector<char> merge;
+				for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
+					const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
+					for (size_t i = 0; i < volumes_and_ranges.size(); ) {
+						int 			   volume_id    = volumes_and_ranges[i].second;
+						const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
+						if (model_volume->is_modifier()) {
+							BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
+							// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
+							std::vector<t_layer_height_range> ranges;
+							ranges.emplace_back(volumes_and_ranges[i].first);
+							size_t j = i + 1;
+							for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
+								ranges.emplace_back(volumes_and_ranges[j].first);
+			                // slicing in parallel
+			                std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, *model_volume);
+			                if (out.empty()) {
+			                	out = std::move(this_slices);
+			                	merge.assign(out.size(), false);
+			                } else {
+			                	for (size_t i = 0; i < out.size(); ++ i)
+			                		if (! this_slices[i].empty())
+			                			if (! out[i].empty()) {
+			                				append(out[i], this_slices[i]);
+			                				merge[i] = true;
+			                			} else
+			                				out[i] = std::move(this_slices[i]);
+			                }
+							i = j;
+						} else
+							++ i;
+					}
+				}
+				for (size_t i = 0; i < merge.size(); ++ i)
+					if (merge[i])
+						out[i] = union_ex(out[i]);
+			}
+		}
+	}
+
+	return out;
 }
 }
 
 
-std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType &model_volume_type) const
 {
 {
     std::vector<const ModelVolume*> volumes;
     std::vector<const ModelVolume*> volumes;
     for (const ModelVolume *volume : this->model_object()->volumes)
     for (const ModelVolume *volume : this->model_object()->volumes)
-        if (volume->is_support_blocker())
+        if (volume->type() == model_volume_type)
             volumes.emplace_back(volume);
             volumes.emplace_back(volume);
     std::vector<float> zs;
     std::vector<float> zs;
     zs.reserve(this->layers().size());
     zs.reserve(this->layers().size());
     for (const Layer *l : this->layers())
     for (const Layer *l : this->layers())
         zs.emplace_back((float)l->slice_z);
         zs.emplace_back((float)l->slice_z);
-    return this->_slice_volumes(zs, volumes);
+    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> PrintObject::slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
 {
 {
     std::vector<ExPolygons> layers;
     std::vector<ExPolygons> layers;
     if (! volumes.empty()) {
     if (! volumes.empty()) {
@@ -1829,34 +1945,71 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
     return layers;
     return layers;
 }
 }
 
 
-std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
+std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
 {
 {
     std::vector<ExPolygons> layers;
     std::vector<ExPolygons> layers;
-    // Compose mesh.
-    //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
-    TriangleMesh mesh(volume.mesh());
-    mesh.transform(volume.get_matrix(), true);
-	if (mesh.repaired) {
-		//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
-		stl_check_facets_exact(&mesh.stl);
+    if (! z.empty()) {
+	    // Compose mesh.
+	    //FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
+	    TriangleMesh mesh(volume.mesh());
+	    mesh.transform(volume.get_matrix(), true);
+		if (mesh.repaired) {
+			//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
+			stl_check_facets_exact(&mesh.stl);
+		}
+	    if (mesh.stl.stats.number_of_facets > 0) {
+	        mesh.transform(m_trafo, true);
+	        // apply XY shift
+	        mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
+	        // perform actual slicing
+	        TriangleMeshSlicer mslicer;
+	        const Print *print = this->print();
+	        auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
+	        // TriangleMeshSlicer needs the shared vertices.
+	        mesh.require_shared_vertices();
+	        mslicer.init(&mesh, callback);
+	        mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
+	        m_print->throw_if_canceled();
+	    }
 	}
 	}
-    if (mesh.stl.stats.number_of_facets > 0) {
-        mesh.transform(m_trafo, true);
-        // apply XY shift
-        mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
-        // perform actual slicing
-        TriangleMeshSlicer mslicer;
-        const Print *print = this->print();
-        auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
-        // TriangleMeshSlicer needs the shared vertices.
-        mesh.require_shared_vertices();
-        mslicer.init(&mesh, callback);
-        mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
-        m_print->throw_if_canceled();
-    }
     return layers;
     return layers;
 }
 }
 
 
+// Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping.
+std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const
+{
+	std::vector<ExPolygons> out;
+	if (! z.empty() && ! ranges.empty()) {
+		if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) {
+			// All layers fit into a single range.
+			out = this->slice_volume(z, volume);
+		} else {
+			std::vector<float> 					   z_filtered;
+			std::vector<std::pair<size_t, size_t>> n_filtered;
+			z_filtered.reserve(z.size());
+			n_filtered.reserve(2 * ranges.size());
+			size_t i = 0;
+			for (const t_layer_height_range &range : ranges) {
+				for (; i < z.size() && z[i] < range.first; ++ i) ;
+				size_t first = i;
+				for (; i < z.size() && z[i] < range.second; ++ i)
+					z_filtered.emplace_back(z[i]);
+				if (i > first)
+					n_filtered.emplace_back(std::make_pair(first, i));
+			}
+			if (! n_filtered.empty()) {
+				std::vector<ExPolygons> layers = this->slice_volume(z_filtered, volume);
+				out.assign(z.size(), ExPolygons());
+				i = 0;
+				for (const std::pair<size_t, size_t> &span : n_filtered)
+					for (size_t j = span.first; j < span.second; ++ j)
+						out[j] = std::move(layers[i ++]);
+			}
+		}
+	}
+	return out;
+}
+
 std::string PrintObject::_fix_slicing_errors()
 std::string PrintObject::_fix_slicing_errors()
 {
 {
     // Collect layers with slicing errors.
     // Collect layers with slicing errors.
@@ -2120,7 +2273,7 @@ void PrintObject::clip_fill_surfaces()
             //Should the pw not be half of the current value?
             //Should the pw not be half of the current value?
             float pw = FLT_MAX;
             float pw = FLT_MAX;
             for (const LayerRegion *layerm : layer->m_regions)
             for (const LayerRegion *layerm : layer->m_regions)
-                pw = std::min<float>(pw, layerm->flow(frPerimeter).scaled_width());
+                pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
             // Append such thick perimeters to the areas that need support
             // Append such thick perimeters to the areas that need support
             polygons_append(overhangs, offset2(perimeters, -pw, +pw));
             polygons_append(overhangs, offset2(perimeters, -pw, +pw));
         }
         }

+ 13 - 9
src/libslic3r/Slicing.cpp

@@ -153,29 +153,33 @@ SlicingParameters SlicingParameters::create_from_config(
     return params;
     return params;
 }
 }
 
 
-// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
+std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges)
+{
+	std::vector<std::pair<t_layer_height_range, coordf_t>> out;
+	out.reserve(config_ranges.size());
+	for (const auto &kvp : config_ranges)
+		out.emplace_back(kvp.first, kvp.second.option("layer_height")->getFloat());
+	return out;
+}
+
+// Convert layer_config_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
 // in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
 // in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
 std::vector<coordf_t> layer_height_profile_from_ranges(
 std::vector<coordf_t> layer_height_profile_from_ranges(
 	const SlicingParameters 	&slicing_params,
 	const SlicingParameters 	&slicing_params,
-//	const t_layer_height_ranges &layer_height_ranges) 
 	const t_layer_config_ranges &layer_config_ranges)                           // #ys_FIXME_experiment
 	const t_layer_config_ranges &layer_config_ranges)                           // #ys_FIXME_experiment
 {
 {
     // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
     // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
     std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
     std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
-//     ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
     ranges_non_overlapping.reserve(layer_config_ranges.size() * 4);             // #ys_FIXME_experiment
     ranges_non_overlapping.reserve(layer_config_ranges.size() * 4);             // #ys_FIXME_experiment
     if (slicing_params.first_object_layer_height_fixed())
     if (slicing_params.first_object_layer_height_fixed())
         ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
         ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
             t_layer_height_range(0., slicing_params.first_object_layer_height), 
             t_layer_height_range(0., slicing_params.first_object_layer_height), 
             slicing_params.first_object_layer_height));
             slicing_params.first_object_layer_height));
     // The height ranges are sorted lexicographically by low / high layer boundaries.
     // The height ranges are sorted lexicographically by low / high layer boundaries.
-//     for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
-    for (t_layer_config_ranges::const_iterator it_range = layer_config_ranges.begin(); 
-                                               it_range != layer_config_ranges.end(); ++ it_range) { // #ys_FIXME_experiment
+    for (t_layer_config_ranges::const_iterator it_range = layer_config_ranges.begin(); it_range != layer_config_ranges.end(); ++ it_range) {
         coordf_t lo = it_range->first.first;
         coordf_t lo = it_range->first.first;
         coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
         coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
-//         coordf_t height = it_range->second;
-        coordf_t height = it_range->second.option("layer_height")->getFloat();  // #ys_FIXME_experiment
+        coordf_t height = it_range->second.option("layer_height")->getFloat();
         if (! ranges_non_overlapping.empty())
         if (! ranges_non_overlapping.empty())
             // Trim current low with the last high.
             // Trim current low with the last high.
             lo = std::max(lo, ranges_non_overlapping.back().first.second);
             lo = std::max(lo, ranges_non_overlapping.back().first.second);
@@ -224,7 +228,7 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
 // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
 // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
 std::vector<coordf_t> layer_height_profile_adaptive(
 std::vector<coordf_t> layer_height_profile_adaptive(
     const SlicingParameters     &slicing_params,
     const SlicingParameters     &slicing_params,
-    const t_layer_height_ranges &layer_height_ranges,
+    const t_layer_config_ranges & /* layer_config_ranges */,
     const ModelVolumePtrs		&volumes)
     const ModelVolumePtrs		&volumes)
 {
 {
     // 1) Initialize the SlicingAdaptive class with the object meshes.
     // 1) Initialize the SlicingAdaptive class with the object meshes.

+ 3 - 3
src/libslic3r/Slicing.hpp

@@ -130,17 +130,17 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters
 }
 }
 
 
 typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
 typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
-typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
 typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
 typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
 
 
+extern std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges);
+
 extern std::vector<coordf_t> layer_height_profile_from_ranges(
 extern std::vector<coordf_t> layer_height_profile_from_ranges(
     const SlicingParameters     &slicing_params,
     const SlicingParameters     &slicing_params,
-//     const t_layer_height_ranges &layer_height_ranges);
     const t_layer_config_ranges &layer_config_ranges);
     const t_layer_config_ranges &layer_config_ranges);
 
 
 extern std::vector<coordf_t> layer_height_profile_adaptive(
 extern std::vector<coordf_t> layer_height_profile_adaptive(
     const SlicingParameters     &slicing_params,
     const SlicingParameters     &slicing_params,
-    const t_layer_height_ranges &layer_height_ranges,
+    const t_layer_config_ranges &layer_config_ranges,
     const ModelVolumePtrs       &volumes);
     const ModelVolumePtrs       &volumes);
 
 
 
 

Some files were not shown because too many files changed in this diff