From 65772958b75ad062e9208ee5d0ef93f5d81df8d7 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 13 Dec 2019 13:43:16 +0100
Subject: [PATCH 01/17] Improved adaptive layer height metrics: Using the area
 of a triangle delimited by the extrusion stepping and the sloping surface.
 This leads to a yet different metric from Cura or upstream Slic3r.

---
 src/libslic3r/Slicing.cpp         |  98 +++++-------
 src/libslic3r/Slicing.hpp         |  14 +-
 src/libslic3r/SlicingAdaptive.cpp | 246 ++++++++++++++++++------------
 src/libslic3r/SlicingAdaptive.hpp |  48 +++---
 src/slic3r/GUI/GLCanvas3D.cpp     |  25 ++-
 src/slic3r/GUI/GLCanvas3D.hpp     |   8 +-
 6 files changed, 218 insertions(+), 221 deletions(-)

diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp
index 9af6048ab..8199bde03 100644
--- a/src/libslic3r/Slicing.cpp
+++ b/src/libslic3r/Slicing.cpp
@@ -224,38 +224,14 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
 
 // Based on the work of @platsch
 // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slicing_params,
-    const ModelObject& object, float cusp_value)
-#else
-std::vector<coordf_t> layer_height_profile_adaptive(
-    const SlicingParameters     &slicing_params,
-    const t_layer_config_ranges & /* layer_config_ranges */,
-    const ModelVolumePtrs		&volumes)
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
+std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slicing_params, const ModelObject& object, float quality_factor)
 {
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
     // 1) Initialize the SlicingAdaptive class with the object meshes.
     SlicingAdaptive as;
     as.set_slicing_parameters(slicing_params);
-    as.set_object(object);
-#else
-    // 1) Initialize the SlicingAdaptive class with the object meshes.
-    SlicingAdaptive as;
-    as.set_slicing_parameters(slicing_params);
-    for (const ModelVolume* volume : volumes)
-        if (volume->is_model_part())
-            as.add_mesh(&volume->mesh());
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-
-    as.prepare();
+    as.prepare(object);
 
     // 2) Generate layers using the algorithm of @platsch 
-    // loop until we have at least one layer and the max slice_z reaches the object height
-#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    double cusp_value = 0.2;
-#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-
     std::vector<double> layer_height_profile;
     layer_height_profile.push_back(0.0);
     layer_height_profile.push_back(slicing_params.first_object_layer_height);
@@ -263,39 +239,41 @@ std::vector<coordf_t> layer_height_profile_adaptive(
         layer_height_profile.push_back(slicing_params.first_object_layer_height);
         layer_height_profile.push_back(slicing_params.first_object_layer_height);
     }
-    double slice_z = slicing_params.first_object_layer_height;
-    int current_facet = 0;
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    while (slice_z <= slicing_params.object_print_z_height()) {
-        double height = slicing_params.max_layer_height;
-#else
-    double height = slicing_params.first_object_layer_height;
-    while ((slice_z - height) <= slicing_params.object_print_z_height()) {
-        height = 999.0;
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
+    double print_z = slicing_params.first_object_layer_height;
+    // last facet visited by the as.next_layer_height() function, where the facets are sorted by their increasing Z span.
+    size_t current_facet = 0;
+    // loop until we have at least one layer and the max slice_z reaches the object height
+    while (print_z + EPSILON < slicing_params.object_print_z_height()) {
+        float height = slicing_params.max_layer_height;
         // Slic3r::debugf "\n Slice layer: %d\n", $id;
         // determine next layer height
-        double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet);
+        float cusp_height = as.next_layer_height(float(print_z), quality_factor, current_facet);
 
+#if 0
         // check for horizontal features and object size
-        /*
-        if($self->config->get_value('match_horizontal_surfaces')) {
-            my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height);
-            if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) {
-                Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist;
-                # can we shrink the current layer a bit?
-                if($cusp_height-($min_height-$horizontal_dist) > $min_height) {
-                    # yes we can
-                    $cusp_height = $cusp_height-($min_height-$horizontal_dist);
-                    Slic3r::debugf "Shrink layer height to %f\n", $cusp_height;
-                }else{
-                    # no, current layer would become too thin
-                    $cusp_height = $cusp_height+$horizontal_dist;
-                    Slic3r::debugf "Widen layer height to %f\n", $cusp_height;
+        if (this->config.match_horizontal_surfaces.value) {
+            coordf_t horizontal_dist = as.horizontal_facet_distance(print_z + height, min_layer_height);
+            if ((horizontal_dist < min_layer_height) && (horizontal_dist > 0)) {
+                #ifdef SLIC3R_DEBUG
+                std::cout << "Horizontal feature ahead, distance: " << horizontal_dist << std::endl;
+                #endif
+                // can we shrink the current layer a bit?
+                if (height-(min_layer_height - horizontal_dist) > min_layer_height) {
+                    // yes we can
+                    height -= (min_layer_height - horizontal_dist);
+                    #ifdef SLIC3R_DEBUG
+                    std::cout << "Shrink layer height to " << height << std::endl;
+                    #endif
+                } else {
+                    // no, current layer would become too thin
+                    height += horizontal_dist;
+                    #ifdef SLIC3R_DEBUG
+                    std::cout << "Widen layer height to " << height << std::endl;
+                    #endif
                 }
             }
         }
-        */
+#endif
         height = std::min(cusp_height, height);
 
         // apply z-gradation
@@ -308,22 +286,22 @@ std::vector<coordf_t> layer_height_profile_adaptive(
     
         // look for an applicable custom range
         /*
-        if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
+        if (my $range = first { $_->[0] <= $print_z && $_->[1] > $print_z } @{$self->layer_height_ranges}) {
             $height = $range->[2];
     
             # if user set custom height to zero we should just skip the range and resume slicing over it
             if ($height == 0) {
-                $slice_z += $range->[1] - $range->[0];
+                $print_z += $range->[1] - $range->[0];
                 next;
             }
         }
         */
         
-        layer_height_profile.push_back(slice_z);
+        layer_height_profile.push_back(print_z);
         layer_height_profile.push_back(height);
-        slice_z += height;
+        print_z += height;
 #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-        layer_height_profile.push_back(slice_z);
+        layer_height_profile.push_back(print_z);
         layer_height_profile.push_back(height);
 #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
     }
@@ -722,11 +700,7 @@ int generate_layer_height_texture(
             const Vec3crd &color1 = palette_raw[idx1];
             const Vec3crd &color2 = palette_raw[idx2];
             coordf_t z = cell_to_z * coordf_t(cell);
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-            assert((lo - EPSILON <= z) && (z <= hi + EPSILON));
-#else
-            assert(z >= lo && z <= hi);
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
+            assert(lo - EPSILON <= z && z <= hi + EPSILON);
             // Intensity profile to visualize the layers.
             coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
             // Color mapping from layer height to RGB.
diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp
index 03ef7e67d..036344b22 100644
--- a/src/libslic3r/Slicing.hpp
+++ b/src/libslic3r/Slicing.hpp
@@ -18,12 +18,7 @@ namespace Slic3r
 
 class PrintConfig;
 class PrintObjectConfig;
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 class ModelObject;
-#else
-class ModelVolume;
-typedef std::vector<ModelVolume*> ModelVolumePtrs;
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
 // Parameters to guide object slicing and support generation.
 // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
@@ -142,10 +137,9 @@ extern std::vector<coordf_t> layer_height_profile_from_ranges(
     const SlicingParameters     &slicing_params,
     const t_layer_config_ranges &layer_config_ranges);
 
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 extern std::vector<double> layer_height_profile_adaptive(
     const SlicingParameters& slicing_params,
-    const ModelObject& object, float cusp_value);
+    const ModelObject& object, float quality_factor);
 
 struct HeightProfileSmoothingParams
 {
@@ -159,12 +153,6 @@ struct HeightProfileSmoothingParams
 extern std::vector<double> smooth_height_profile(
     const std::vector<double>& profile, const SlicingParameters& slicing_params,
     const HeightProfileSmoothingParams& smoothing_params);
-#else
-extern std::vector<coordf_t> layer_height_profile_adaptive(
-    const SlicingParameters     &slicing_params,
-    const t_layer_config_ranges &layer_config_ranges,
-    const ModelVolumePtrs       &volumes);
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
 enum LayerHeightEditActionType : unsigned int {
     LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp
index b6ebf1ac0..7ab0c47b2 100644
--- a/src/libslic3r/SlicingAdaptive.cpp
+++ b/src/libslic3r/SlicingAdaptive.cpp
@@ -1,156 +1,211 @@
 #include "libslic3r.h"
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 #include "Model.hpp"
-#else
 #include "TriangleMesh.hpp"
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 #include "SlicingAdaptive.hpp"
 
+#include <boost/log/trivial.hpp>
+
+// Based on the work of Florens Waserfall (@platch on github)
+// and his paper
+// Florens Wasserfall, Norman Hendrich, Jianwei Zhang:
+// Adaptive Slicing for the FDM Process Revisited
+// 13th IEEE Conference on Automation Science and Engineering (CASE-2017), August 20-23, Xi'an, China. DOI: 10.1109/COASE.2017.8256074
+// https://tams.informatik.uni-hamburg.de/publications/2017/Adaptive%20Slicing%20for%20the%20FDM%20Process%20Revisited.pdf
+
+// Vojtech believes that there is a bug in @platch's derivation of the triangle area error metric.
+// Following Octave code paints graphs of recommended layer height versus surface slope angle.
+#if 0
+adeg=0:1:85;
+a=adeg*pi/180;
+t=tan(a);
+tsqr=sqrt(tan(a));
+lerr=1./cos(a);
+lerr2=1./(0.3+cos(a));
+plot(adeg, t, 'b', adeg, sqrt(t), 'g', adeg, 0.5 * lerr, 'm', adeg, 0.5 * lerr2, 'r')
+xlabel("angle(deg), 0 - horizontal wall, 90 - vertical wall");
+ylabel("layer height");
+legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as PrusaSlicer - error triangle area limit", "old slic3r - max distance metric", "new slic3r - Waserfall paper");
+#endif
+
+#ifndef NDEBUG
+	#define ADAPTIVE_LAYER_HEIGHT_DEBUG
+#endif /* NDEBUG */
+
 namespace Slic3r
 {
 
-#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-void SlicingAdaptive::clear()
-{
-    m_meshes.clear();
-	m_faces.clear();
-	m_face_normal_z.clear();
-}
-#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-
-std::pair<float, float> face_z_span(const stl_facet *f)
+static inline std::pair<float, float> face_z_span(const stl_facet &f)
 {
 	return std::pair<float, float>(
-		std::min(std::min(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)),
-		std::max(std::max(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)));
+		std::min(std::min(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)),
+		std::max(std::max(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)));
 }
 
-void SlicingAdaptive::prepare()
+// By Florens Waserfall aka @platch:
+// This constant essentially describes the volumetric error at the surface which is induced 
+// by stacking "elliptic" extrusion threads. It is empirically determined by
+// 1. measuring the surface profile of printed parts to find
+// the ratio between layer height and profile height and then
+// 2. computing the geometric difference between the model-surface and the elliptic profile.
+//
+// The definition of the roughness formula is in 
+// https://tams.informatik.uni-hamburg.de/publications/2017/Adaptive%20Slicing%20for%20the%20FDM%20Process%20Revisited.pdf
+// (page 51, formula (8))
+// Currenty @platch's error metric formula is not used.
+static constexpr double SURFACE_CONST = 0.18403;
+
+// for a given facet, compute maximum height within the allowed surface roughness / stairstepping deviation
+static inline float layer_height_from_slope(const SlicingAdaptive::FaceZ &face, float max_surface_deviation)
 {
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    if (m_object == nullptr)
-        return;
+// @platch's formula, see his paper "Adaptive Slicing for the FDM Process Revisited".
+//    return float(max_surface_deviation / (SURFACE_CONST + 0.5 * std::abs(normal_z)));
+	
+// Constant stepping in horizontal direction, as used by Cura.
+//    return (face.n_cos > 1e-5) ? float(max_surface_deviation * face.n_sin / face.n_cos) : FLT_MAX;
 
-    m_faces.clear();
-    m_face_normal_z.clear();
+// Constant error measured as an area of the surface error triangle, Vojtech's formula.
+//    return (face.n_cos > 1e-5) ? float(1.44 * max_surface_deviation * sqrt(face.n_sin / face.n_cos)) : FLT_MAX;
 
-    m_mesh = m_object->raw_mesh();
-    const ModelInstance* first_instance = m_object->instances.front();
-    m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed());
+// Constant error measured as an area of the surface error triangle, Vojtech's formula with clamping to roughness at 90 degrees.
+    return std::min(max_surface_deviation / 0.184f, (face.n_cos > 1e-5) ? float(1.44 * max_surface_deviation * sqrt(face.n_sin / face.n_cos)) : FLT_MAX);
+
+// Constant stepping along the surface, equivalent to the "surface roughness" metric by Perez and later Pandey et all, see @platch's paper for references.
+//    return float(max_surface_deviation * face.n_sin);
+}
+
+void SlicingAdaptive::clear()
+{
+	m_faces.clear();
+}
+
+void SlicingAdaptive::prepare(const ModelObject &object)
+{
+    this->clear();
+
+    TriangleMesh		 mesh			= object.raw_mesh();
+    const ModelInstance &first_instance = *object.instances.front();
+    mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed());
 
     // 1) Collect faces from mesh.
-    m_faces.reserve(m_mesh.stl.stats.number_of_facets);
-    for (stl_facet& face : m_mesh.stl.facet_start)
-    {
-        face.normal.normalize();
-        m_faces.emplace_back(&face);
+    m_faces.reserve(mesh.stl.stats.number_of_facets);
+    for (const stl_facet &face : mesh.stl.facet_start) {
+    	Vec3f n = face.normal.normalized();
+		m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
     }
-#else
-    // 1) Collect faces of all meshes.
-    int nfaces_total = 0;
-    for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
-		nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
-    m_faces.reserve(nfaces_total);
-    for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
-        for (const stl_facet& face : (*it_mesh)->stl.facet_start)
-            m_faces.emplace_back(&face);
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
 	// 2) Sort faces lexicographically by their Z span.
-	std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { return face_z_span(f1) < face_z_span(f2); });
-
-	// 3) Generate Z components of the facet normals.
-	m_face_normal_z.assign(m_faces.size(), 0.0f);
-    for (size_t iface = 0; iface < m_faces.size(); ++ iface)
-    	m_face_normal_z[iface] = m_faces[iface]->normal(2);
+	std::sort(m_faces.begin(), m_faces.end(), [](const FaceZ &f1, const FaceZ &f2) { return f1.z_span < f2.z_span; });
 }
 
-float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet)
+// current_facet is in/out parameter, rememebers the index of the last face of m_faces visited, 
+// where this function will start from.
+// print_z - the top print surface of the previous layer.
+// returns height of the next layer.
+float SlicingAdaptive::next_layer_height(const float print_z, float quality_factor, size_t &current_facet)
 {
-	float height = (float)m_slicing_params.max_layer_height;
-	bool first_hit = false;
+	float  height = (float)m_slicing_params.max_layer_height;
+
+	float  max_surface_deviation;
+
+	{
+#if 0
+// @platch's formula for quality:
+	    double delta_min = SURFACE_CONST * m_slicing_params.min_layer_height;
+	    double delta_mid = (SURFACE_CONST + 0.5) * m_slicing_params.layer_height;
+	    double delta_max = (SURFACE_CONST + 0.5) * m_slicing_params.max_layer_height;
+#else
+// Vojtech's formula for triangle area error metric.
+	    double delta_min = m_slicing_params.min_layer_height;
+	    double delta_mid = m_slicing_params.layer_height;
+	    double delta_max = m_slicing_params.max_layer_height;
+#endif
+	    max_surface_deviation = (quality_factor < 0.5f) ?
+	    	lerp(delta_min, delta_mid, 2. * quality_factor) :
+	    	lerp(delta_max, delta_mid, 2. * (1. - quality_factor));
+	}
 	
 	// find all facets intersecting the slice-layer
-	int ordered_id = current_facet;
-	for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
-        std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
-        // facet's minimum is higher than slice_z -> end loop
-		if (zspan.first >= z)
-			break;
-		// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
-		if (zspan.second > z) {
-			// first event?
-			if (! first_hit) {
-				first_hit = true;
-				current_facet = ordered_id;
-            }
-			// skip touching facets which could otherwise cause small cusp values
-			if (zspan.second <= z + EPSILON)
-				continue;
-			// compute cusp-height for this facet and store minimum of all heights
-			float normal_z = m_face_normal_z[ordered_id];
-            height = std::min(height, (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z));
-        }
+	size_t ordered_id = current_facet;
+	{
+		bool first_hit = false;
+		for (; ordered_id < m_faces.size(); ++ ordered_id) {
+	        const std::pair<float, float> &zspan = m_faces[ordered_id].z_span;
+	        // facet's minimum is higher than slice_z -> end loop
+			if (zspan.first >= print_z)
+				break;
+			// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
+			if (zspan.second > print_z) {
+				// first event?
+				if (! first_hit) {
+					first_hit = true;
+					current_facet = ordered_id;
+	            }
+				// skip touching facets which could otherwise cause small cusp values
+				if (zspan.second < print_z + EPSILON)
+					continue;
+				// compute cusp-height for this facet and store minimum of all heights
+				height = std::min(height, layer_height_from_slope(m_faces[ordered_id], max_surface_deviation));
+	        }
+		}
 	}
 
 	// lower height limit due to printer capabilities
 	height = std::max(height, float(m_slicing_params.min_layer_height));
 
 	// check for sloped facets inside the determined layer and correct height if necessary
-	if (height > m_slicing_params.min_layer_height) {
-		for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
-            std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
+	if (height > float(m_slicing_params.min_layer_height)) {
+		for (; ordered_id < m_faces.size(); ++ ordered_id) {
+            const std::pair<float, float> &zspan = m_faces[ordered_id].z_span;
             // facet's minimum is higher than slice_z + height -> end loop
-			if (zspan.first >= z + height)
+			if (zspan.first >= print_z + height)
 				break;
 
 			// skip touching facets which could otherwise cause small cusp values
-			if (zspan.second <= z + EPSILON)
+			if (zspan.second < print_z + EPSILON)
 				continue;
 
 			// Compute cusp-height for this facet and check against height.
-			float normal_z = m_face_normal_z[ordered_id];
-            float cusp = (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z);
+            float reduced_height = layer_height_from_slope(m_faces[ordered_id], max_surface_deviation);
 
-			float z_diff = zspan.first - z;
-
-			// handle horizontal facets
-            if (normal_z > 0.999f) {
-                // Slic3r::debugf "cusp computation, height is reduced from %f", $height;
+			float z_diff = zspan.first - print_z;
+			if (reduced_height < z_diff) {
+				assert(z_diff < height + EPSILON);
+				// The currently visited triangle's slope limits the next layer height so much, that
+				// the lowest point of the currently visible triangle is already above the newly proposed layer height.
+				// This means, that we need to limit the layer height so that the offending newly visited triangle
+				// is just above of the new layer.
+#ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG
+                BOOST_LOG_TRIVIAL(trace) << "cusp computation, height is reduced from " << height << "to " << z_diff << " due to z-diff";
+#endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */
 				height = z_diff;
-				// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
-			} else if (cusp > z_diff) {
-				if (cusp < height) {
-					// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
-					height = cusp;
-					// Slic3r::debugf "to %f due to new cusp height\n", $height;
-				}
-			} else {
-				// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
-				height = z_diff;
-				// Slic3r::debugf "to z-diff: %f\n", $height;
+			} else if (reduced_height < height) {
+#ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG
+				BOOST_LOG_TRIVIAL(trace) << "adaptive layer computation: height is reduced from " << height << "to " << reduced_height << " due to higher facet";
+#endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */
+				height = reduced_height;
 			}
 		}
 		// lower height limit due to printer capabilities again
 		height = std::max(height, float(m_slicing_params.min_layer_height));
 	}
 
-//	Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;	
+#ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG
+    BOOST_LOG_TRIVIAL(trace) << "adaptive layer computation, layer-bottom at z:" << print_z << ", quality_factor:" << quality_factor << ", resulting layer height:" << height;
+#endif  /* ADAPTIVE_LAYER_HEIGHT_DEBUG */
 	return height; 
 }
 
-#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 // Returns the distance to the next horizontal facet in Z-dir 
 // to consider horizontal object features in slice thickness
 float SlicingAdaptive::horizontal_facet_distance(float z)
 {
 	for (size_t i = 0; i < m_faces.size(); ++ i) {
-        std::pair<float, float> zspan = face_z_span(m_faces[i]);
+        std::pair<float, float> zspan = m_faces[i].z_span;
         // facet's minimum is higher than max forward distance -> end loop
 		if (zspan.first > z + m_slicing_params.max_layer_height)
 			break;
 		// min_z == max_z -> horizontal facet
-		if ((zspan.first > z) && (zspan.first == zspan.second))
+		if (zspan.first > z && zspan.first == zspan.second)
 			return zspan.first - z;
 	}
 	
@@ -158,6 +213,5 @@ float SlicingAdaptive::horizontal_facet_distance(float z)
 	return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? 
 		std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height;
 }
-#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
 }; // namespace Slic3r
diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp
index 1d2996986..a296553d6 100644
--- a/src/libslic3r/SlicingAdaptive.hpp
+++ b/src/libslic3r/SlicingAdaptive.hpp
@@ -5,50 +5,36 @@
 
 #include "Slicing.hpp"
 #include "admesh/stl.h"
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-#include "TriangleMesh.hpp"
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
 namespace Slic3r
 {
 
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 class ModelVolume;
-#else
-class TriangleMesh;
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
 class SlicingAdaptive
 {
 public:
-#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    void clear();
-#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    void set_object(const ModelObject& object) { m_object = &object; }
-#else
-    void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); }
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    void prepare();
-	float cusp_height(float z, float cusp_value, int &current_facet);
-#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
+    void  clear();
+    void  set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
+    void  prepare(const ModelObject &object);
+    // Return next layer height starting from the last print_z, using a quality measure
+    // (quality in range from 0 to 1, 0 - highest quality at low layer heights, 1 - lowest print quality at high layer heights).
+    // The layer height curve shall be centered roughly around the default profile's layer height for quality 0.5.
+	float next_layer_height(const float print_z, float quality, size_t &current_facet);
     float horizontal_facet_distance(float z);
-#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
+
+	struct FaceZ {
+		std::pair<float, float> z_span;
+		// Cosine of the normal vector towards the Z axis.
+		float					n_cos;
+		// Sine of the normal vector towards the Z axis.
+		float					n_sin;
+	};
 
 protected:
-	SlicingParameters 					m_slicing_params;
+	SlicingParameters 		m_slicing_params;
 
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    const ModelObject*                  m_object;
-    TriangleMesh                        m_mesh;
-#else
-    std::vector<const TriangleMesh*>	m_meshes;
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    // Collected faces of all meshes, sorted by raising Z of the bottom most face.
-	std::vector<const stl_facet*>		m_faces;
-    // Z component of face normals, normalized.
-	std::vector<float>					m_face_normal_z;
+	std::vector<FaceZ>		m_faces;
 };
 
 }; // namespace Slic3r
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 71ce173df..012127aed 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -133,7 +133,7 @@ GLCanvas3D::LayersEditing::LayersEditing()
     , m_slicing_parameters(nullptr)
     , m_layer_height_profile_modified(false)
 #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    , m_adaptive_cusp(0.0f)
+    , m_adaptive_quality(0.5f)
 #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
     , state(Unknown)
     , band_width(2.0f)
@@ -268,24 +268,24 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
     
     ImGui::Separator();
     if (imgui.button(_(L("Adaptive"))))
-        wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp));
+        wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_quality));
 
     ImGui::SameLine();
     float text_align = ImGui::GetCursorPosX();
     ImGui::AlignTextToFramePadding();
-    imgui.text(_(L("Cusp (mm)")));
+    imgui.text(_(L("Quality / Speed")));
     if (ImGui::IsItemHovered())
     {
         ImGui::BeginTooltip();
-        ImGui::TextUnformatted(_(L("I am a tooltip")));
+        ImGui::TextUnformatted(_(L("Higher print quality versus higher print speed.")));
         ImGui::EndTooltip();
     }
 
     ImGui::SameLine();
     float widget_align = ImGui::GetCursorPosX();
     ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
-    m_adaptive_cusp = clamp(0.0f, 0.5f * (float)m_slicing_parameters->layer_height, m_adaptive_cusp);
-    ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, 0.5f * (float)m_slicing_parameters->layer_height, "%.3f");
+    m_adaptive_quality = clamp(0.0f, 1.f, m_adaptive_quality);
+    ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f");
 
     ImGui::Separator();
     if (imgui.button(_(L("Smooth"))))
@@ -645,10 +645,10 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas)
 }
 
 #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp)
+void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor)
 {
     this->update_slicing_parameters();
-    m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp);
+    m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, quality_factor);
     const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
     m_layers_texture.valid = false;
     canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
@@ -712,11 +712,6 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters()
 		m_slicing_parameters = new SlicingParameters();
     	*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
     }
-
-#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-    if (m_adaptive_cusp == 0.0f)
-        m_adaptive_cusp = 0.25f * m_slicing_parameters->layer_height;
-#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 }
 
 float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas)
@@ -1690,10 +1685,10 @@ void GLCanvas3D::reset_layer_height_profile()
     m_dirty = true;
 }
 
-void GLCanvas3D::adaptive_layer_height_profile(float cusp)
+void GLCanvas3D::adaptive_layer_height_profile(float quality_factor)
 {
     wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Adaptive")));
-    m_layers_editing.adaptive_layer_height_profile(*this, cusp);
+    m_layers_editing.adaptive_layer_height_profile(*this, quality_factor);
     m_layers_editing.state = LayersEditing::Completed;
     m_dirty = true;
 }
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 2ff65052a..547a9d885 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -185,7 +185,7 @@ private:
         bool                        m_layer_height_profile_modified;
 
 #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-        mutable float               m_adaptive_cusp;
+        mutable float               m_adaptive_quality;
         mutable HeightProfileSmoothingParams m_smooth_params;
 #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
@@ -236,8 +236,8 @@ private:
 		void accept_changes(GLCanvas3D& canvas);
         void reset_layer_height_profile(GLCanvas3D& canvas);
 #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
-        void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp);
-        void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn);
+        void adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor);
+        void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params);
 #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 
         static float get_cursor_z_relative(const GLCanvas3D& canvas);
@@ -539,7 +539,7 @@ public:
 
 #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
     void reset_layer_height_profile();
-    void adaptive_layer_height_profile(float cusp);
+    void adaptive_layer_height_profile(float quality_factor);
     void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params);
 #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 

From 34b6f7362a9de43c706b5e4c7689f3035950312a Mon Sep 17 00:00:00 2001
From: Yuri D'Elia <wavexx@thregr.org>
Date: Sun, 8 Dec 2019 16:08:39 +0100
Subject: [PATCH 02/17] Add dependency on -ldl to hidapi (for dlclose) on linux

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

diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt
index 1f53c9b69..f3045466e 100644
--- a/src/hidapi/CMakeLists.txt
+++ b/src/hidapi/CMakeLists.txt
@@ -15,5 +15,5 @@ add_library(hidapi STATIC ${HIDAPI_IMPL})
 if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
 	# Don't link the udev library, as there are two versions out there (libudev.so.0, libudev.so.1), so they are linked explicitely.
 #	target_link_libraries(hidapi udev)
-	target_link_libraries(hidapi)
+	target_link_libraries(hidapi dl)
 endif()

From b061904ad8d4c136c5aa1f7a78d98a74dc31bab2 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 13 Dec 2019 15:40:42 +0100
Subject: [PATCH 03/17] Fixes by @supermerill from pull request "ShortestPath
 fix" #3306

---
 src/libslic3r/ShortestPath.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 8b5cbf483..39b82f4ce 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -43,6 +43,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<En
 		assert(next_idx < end_points.size());
 		EndPointType &end_point = end_points[next_idx];
 		end_point.chain_id = 1;
+		out.emplace_back(next_idx / 2, (next_idx & 1) != 0);
 		this_idx = next_idx ^ 1;
 	}
 #ifndef NDEBUG
@@ -72,7 +73,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals
 	else if (num_segments == 1)
 	{
 		// Just sort the end points so that the first point visited is closest to start_near.
-		out.emplace_back(0, start_near != nullptr && 
+		out.emplace_back(0, could_reverse_func(0) && start_near != nullptr && 
             (end_point_func(0, true) - *start_near).template cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).template cast<double>().squaredNorm());
 	} 
 	else

From 06c9d3ad560146ec6d3de19683c6a8b6075b148b Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 13 Dec 2019 17:46:35 +0100
Subject: [PATCH 04/17] Added readme.md file for hidapi

---
 src/hidapi/README.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 src/hidapi/README.md

diff --git a/src/hidapi/README.md b/src/hidapi/README.md
new file mode 100644
index 000000000..4f66b3274
--- /dev/null
+++ b/src/hidapi/README.md
@@ -0,0 +1,5 @@
+** hidapi is a c++ library for communicating with USB and Bluetooth HID devices on Linux, Mac and Windows.**
+
+For more information go to https://github.com/libusb/hidapi
+
+THIS DIRECTORY CONTAINS THE hidapi-0.9.0 7da5cc9 SOURCE DISTRIBUTION.

From ca518c003f9aa60fa62b5f5770266fa9ee9d7d43 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 16 Dec 2019 10:24:08 +0100
Subject: [PATCH 05/17] Added message dialog informing the user that it is not
 allowed to change the filename when using reload from disk command

---
 src/slic3r/GUI/Plater.cpp | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index c8267331d..861ff47da 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3245,22 +3245,30 @@ void Plater::priv::reload_from_disk()
             input_paths.push_back(sel_filename_path);
             missing_input_paths.pop_back();
 
-            std::string sel_path = fs::path(sel_filename_path).remove_filename().string();
+            fs::path sel_path = fs::path(sel_filename_path).remove_filename().string();
 
             std::vector<fs::path>::iterator it = missing_input_paths.begin();
             while (it != missing_input_paths.end())
             {
                 // try to use the path of the selected file with all remaining missing files
-                std::string repathed_filename = sel_path + "/" + it->filename().string();
+                fs::path repathed_filename = sel_path;
+                repathed_filename /= it->filename();
                 if (fs::exists(repathed_filename))
                 {
-                    input_paths.push_back(repathed_filename);
+                    input_paths.push_back(repathed_filename.string());
                     it = missing_input_paths.erase(it);
                 }
                 else
                     ++it;
             }
         }
+        else
+        {
+            wxString message = _(L("It is not allowed to change the file to reload")) + " (" + from_u8(fs::path(search).filename().string())+ ").\n" + _(L("Do you want to retry")) + " ?";
+            wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
+            if (dlg.ShowModal() != wxID_YES)
+                return;
+        }
     }
 #endif // ENABLE_RELOAD_FROM_DISK_MISSING_SELECTION
 

From 09e15939acb239975362eb64216c91c262c0c471 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 16 Dec 2019 10:34:51 +0100
Subject: [PATCH 06/17] Bumped up version number of Slicer

---
 version.inc | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/version.inc b/version.inc
index 0a62ff6f3..c96a304ab 100644
--- a/version.inc
+++ b/version.inc
@@ -3,7 +3,7 @@
 
 set(SLIC3R_APP_NAME "PrusaSlicer")
 set(SLIC3R_APP_KEY "PrusaSlicer")
-set(SLIC3R_VERSION "2.1.0")
+set(SLIC3R_VERSION "2.2.0-alpha0")
 set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
-set(SLIC3R_RC_VERSION "2,1,0,0")
-set(SLIC3R_RC_VERSION_DOTS "2.1.0.0")
+set(SLIC3R_RC_VERSION "2,2,0,0")
+set(SLIC3R_RC_VERSION_DOTS "2.2.0.0")

From 43213b95480a085f1e9bd75ec439b69d6a7e1a97 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 16 Dec 2019 13:35:45 +0100
Subject: [PATCH 07/17] Added [X] and [Close] buttons to close 3Dconnexion
 setting imgui dialog by clicking on them

---
 src/libslic3r/Technologies.hpp       |   3 +
 src/slic3r/GUI/GLCanvas3D.cpp        |   6 +-
 src/slic3r/GUI/ImGuiWrapper.cpp      |  10 ++
 src/slic3r/GUI/ImGuiWrapper.hpp      |   2 +
 src/slic3r/GUI/Mouse3DController.cpp | 219 +++++++++++++++++----------
 src/slic3r/GUI/Mouse3DController.hpp |  19 ++-
 6 files changed, 178 insertions(+), 81 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 0ec8b36ee..d503f0c64 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -53,4 +53,7 @@
 // Enable selection for missing files in reload from disk command
 #define ENABLE_RELOAD_FROM_DISK_MISSING_SELECTION (1 && ENABLE_2_2_0_ALPHA1)
 
+// Enable closing 3Dconnextion imgui settings dialog by clicking on [X] and [Close] buttons
+#define ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG (1 && ENABLE_2_2_0_ALPHA1)
+
 #endif // _technologies_h_
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 8e922398a..5a0092f8b 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1920,7 +1920,11 @@ void GLCanvas3D::render()
     m_camera.debug_render();
 #endif // ENABLE_CAMERA_STATISTICS
 
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this);
+#else
     wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 
     wxGetApp().imgui()->render();
 
@@ -2628,8 +2632,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
     if (m_extra_frame_requested || mouse3d_controller_applied)
     {
         m_dirty = true;
-        evt.RequestMore();
         m_extra_frame_requested = false;
+        evt.RequestMore();
     }
     else
         m_dirty = false;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 33e526083..90ef017fc 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -254,6 +254,16 @@ bool ImGuiWrapper::begin(const wxString &name, int flags)
     return begin(into_u8(name), flags);
 }
 
+bool ImGuiWrapper::begin(const std::string& name, bool* close, int flags)
+{
+    return ImGui::Begin(name.c_str(), close, (ImGuiWindowFlags)flags);
+}
+
+bool ImGuiWrapper::begin(const wxString& name, bool* close, int flags)
+{
+    return begin(into_u8(name), close, flags);
+}
+
 void ImGuiWrapper::end()
 {
     ImGui::End();
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 7cce60367..5118af036 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -56,6 +56,8 @@ public:
 
     bool begin(const std::string &name, int flags = 0);
     bool begin(const wxString &name, int flags = 0);
+    bool begin(const std::string& name, bool* close, int flags = 0);
+    bool begin(const wxString& name, bool* close, int flags = 0);
     void end();
 
     bool button(const wxString &label);
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index b3d7c0e4f..ad5be88d8 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -5,6 +5,9 @@
 #include "GUI_App.hpp"
 #include "PresetBundle.hpp"
 #include "AppConfig.hpp"
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+#include "GLCanvas3D.hpp"
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 
 #include <wx/glcanvas.h>
 
@@ -184,7 +187,10 @@ Mouse3DController::Mouse3DController()
     , m_device(nullptr)
     , m_device_str("")
     , m_running(false)
-    , m_settings_dialog(false)
+    , m_show_settings_dialog(false)
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    , m_settings_dialog_closed_by_user(false)
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 {
     m_last_time = std::chrono::high_resolution_clock::now();
 }
@@ -229,8 +235,11 @@ bool Mouse3DController::apply(Camera& camera)
     if (!m_running && is_device_connected())
     {
         disconnect_device();
-        // hides the settings dialog if the user re-plug the device
-        m_settings_dialog = false;
+        // hides the settings dialog if the user un-plug the device
+        m_show_settings_dialog = false;
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+        m_settings_dialog_closed_by_user = false;
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
     }
 
     // check if the user plugged the device
@@ -240,88 +249,144 @@ bool Mouse3DController::apply(Camera& camera)
     return is_device_connected() ? m_state.apply(camera) : false;
 }
 
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
+#else
 void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 {
-    if (!m_running || !m_settings_dialog)
+    if (!m_running || !m_show_settings_dialog)
         return;
 
-    ImGuiWrapper& imgui = *wxGetApp().imgui();
-
-    imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f);
-
-    imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
-
-    const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
-    ImGui::PushStyleColor(ImGuiCol_Text, color);
-    imgui.text(_(L("Device:")));
-    ImGui::PopStyleColor();
-    ImGui::SameLine();
-    imgui.text(m_device_str);
-
-    ImGui::Separator();
-    ImGui::PushStyleColor(ImGuiCol_Text, color);
-    imgui.text(_(L("Speed:")));
-    ImGui::PopStyleColor();
-
-    float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
-    if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f"))
-        m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
-
-    float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
-    if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f"))
-        m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
-
-    ImGui::Separator();
-    ImGui::PushStyleColor(ImGuiCol_Text, color);
-    imgui.text(_(L("Deadzone:")));
-    ImGui::PopStyleColor();
-
-    float translation_deadzone = (float)m_state.get_translation_deadzone();
-    if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
-        m_state.set_translation_deadzone((double)translation_deadzone);
-
-    float rotation_deadzone = m_state.get_rotation_deadzone();
-    if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f"))
-        m_state.set_rotation_deadzone(rotation_deadzone);
-
-#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
-    ImGui::Separator();
-    ImGui::Separator();
-    ImGui::PushStyleColor(ImGuiCol_Text, color);
-    imgui.text("DEBUG:");
-    imgui.text("Vectors:");
-    ImGui::PopStyleColor();
-    Vec3f translation = m_state.get_translation().cast<float>();
-    Vec3f rotation = m_state.get_rotation();
-    ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
-    ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
-
-    ImGui::PushStyleColor(ImGuiCol_Text, color);
-    imgui.text("Queue size:");
-    ImGui::PopStyleColor();
-
-    int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() };
-    int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() };
-    int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
-
-    ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly);
-    ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly);
-    ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
-
-    int queue_size = (int)m_state.get_queues_max_size();
-    if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    // when the user clicks on [X] or [Close] button we need to trigger
+    // an extra frame to let the dialog disappear
+    if (m_settings_dialog_closed_by_user)
     {
-        if (queue_size > 0)
-            m_state.set_queues_max_size(queue_size);
+        m_show_settings_dialog = false;
+        m_settings_dialog_closed_by_user = false;
+        canvas.request_extra_frame();
+        return;
     }
 
-    ImGui::Separator();
-    ImGui::PushStyleColor(ImGuiCol_Text, color);
-    imgui.text("Camera:");
-    ImGui::PopStyleColor();
-    Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>();
-    ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
+    Size cnv_size = canvas.get_canvas_size();
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+
+    ImGuiWrapper& imgui = *wxGetApp().imgui();
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f);
+#else
+    imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f);
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    static ImVec2 last_win_size(0.0f, 0.0f);
+    bool shown = true;
+    if (imgui.begin(_(L("3Dconnexion settings")), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse))
+    {
+        if (shown)
+        {
+            ImVec2 win_size = ImGui::GetWindowSize();
+            if ((last_win_size.x != win_size.x) || (last_win_size.y != win_size.y))
+            {
+                // when the user clicks on [X] button, the next time the dialog is shown 
+                // has a dummy size, so we trigger an extra frame to let it have the correct size
+                last_win_size = win_size;
+                canvas.request_extra_frame();
+            }
+#else
+    imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+
+            const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
+            ImGui::PushStyleColor(ImGuiCol_Text, color);
+            imgui.text(_(L("Device:")));
+            ImGui::PopStyleColor();
+            ImGui::SameLine();
+            imgui.text(m_device_str);
+
+            ImGui::Separator();
+            ImGui::PushStyleColor(ImGuiCol_Text, color);
+            imgui.text(_(L("Speed:")));
+            ImGui::PopStyleColor();
+
+            float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
+            if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f"))
+                m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
+
+            float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
+            if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f"))
+                m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
+
+            ImGui::Separator();
+            ImGui::PushStyleColor(ImGuiCol_Text, color);
+            imgui.text(_(L("Deadzone:")));
+            ImGui::PopStyleColor();
+
+            float translation_deadzone = (float)m_state.get_translation_deadzone();
+            if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
+                m_state.set_translation_deadzone((double)translation_deadzone);
+
+            float rotation_deadzone = m_state.get_rotation_deadzone();
+            if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f"))
+                m_state.set_rotation_deadzone(rotation_deadzone);
+
+#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
+            ImGui::Separator();
+            ImGui::Separator();
+            ImGui::PushStyleColor(ImGuiCol_Text, color);
+            imgui.text("DEBUG:");
+            imgui.text("Vectors:");
+            ImGui::PopStyleColor();
+            Vec3f translation = m_state.get_translation().cast<float>();
+            Vec3f rotation = m_state.get_rotation();
+            ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
+            ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
+
+            ImGui::PushStyleColor(ImGuiCol_Text, color);
+            imgui.text("Queue size:");
+            ImGui::PopStyleColor();
+
+            int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() };
+            int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() };
+            int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
+
+            ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly);
+            ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly);
+            ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
+
+            int queue_size = (int)m_state.get_queues_max_size();
+            if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
+            {
+                if (queue_size > 0)
+                    m_state.set_queues_max_size(queue_size);
+            }
+
+            ImGui::Separator();
+            ImGui::PushStyleColor(ImGuiCol_Text, color);
+            imgui.text("Camera:");
+            ImGui::PopStyleColor();
+            Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>();
+            ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
 #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+
+            ImGui::Separator();
+            if (imgui.button(_(L("Close"))))
+            {
+                // the user clicked on the [Close] button
+                m_settings_dialog_closed_by_user = true;
+                canvas.set_as_dirty();
+            }
+        }
+        else
+        {
+            // the user clicked on the [X] button
+            m_settings_dialog_closed_by_user = true;
+            canvas.set_as_dirty();
+        }
+    }
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 
     imgui.end();
 }
diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp
index cc03d4a24..543c44e77 100644
--- a/src/slic3r/GUI/Mouse3DController.hpp
+++ b/src/slic3r/GUI/Mouse3DController.hpp
@@ -18,6 +18,9 @@ namespace Slic3r {
 namespace GUI {
 
 struct Camera;
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+class GLCanvas3D;
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 
 class Mouse3DController
 {
@@ -130,7 +133,13 @@ class Mouse3DController
     hid_device* m_device;
     std::string m_device_str;
     bool m_running;
-    bool m_settings_dialog;
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    mutable bool m_show_settings_dialog;
+    // set to true when ther user closes the dialog by clicking on [X] or [Close] buttons
+    mutable bool m_settings_dialog_closed_by_user;
+#else
+    bool m_show_settings_dialog;
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
     std::chrono::time_point<std::chrono::high_resolution_clock> m_last_time;
 
 public:
@@ -146,9 +155,13 @@ public:
 
     bool apply(Camera& camera);
 
-    bool is_settings_dialog_shown() const { return m_settings_dialog; }
-    void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); }
+    bool is_settings_dialog_shown() const { return m_show_settings_dialog; }
+    void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); }
+#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
+    void render_settings_dialog(GLCanvas3D& canvas) const;
+#else
     void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const;
+#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG
 
 private:
     bool connect_device();

From afcc6bbb08b9a774614b1746bf2c843319ccd0aa Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 17 Dec 2019 08:37:50 +0100
Subject: [PATCH 08/17] Code refactoring for Color change implementation

---
 src/libslic3r/Format/3mf.cpp                | 45 ++++++++++-----------
 src/libslic3r/Format/AMF.cpp                | 14 +++----
 src/libslic3r/GCode.cpp                     | 32 +++++++--------
 src/libslic3r/GCode.hpp                     |  2 +-
 src/libslic3r/GCodeWriter.hpp               |  5 +++
 src/libslic3r/Model.cpp                     | 24 +++++------
 src/libslic3r/Model.hpp                     | 27 ++++++-------
 src/libslic3r/Print.cpp                     |  4 +-
 src/libslic3r/PrintConfig.hpp               |  6 ---
 src/slic3r/GUI/BackgroundSlicingProcess.cpp |  4 +-
 src/slic3r/GUI/GLCanvas3D.cpp               | 41 ++++++++++---------
 src/slic3r/GUI/GUI_Preview.cpp              | 10 ++---
 src/slic3r/GUI/Plater.cpp                   | 13 ++++--
 src/slic3r/GUI/PresetBundle.cpp             |  1 +
 src/slic3r/GUI/wxExtensions.cpp             | 42 +++++++++----------
 src/slic3r/GUI/wxExtensions.hpp             | 17 ++------
 16 files changed, 139 insertions(+), 148 deletions(-)

diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 89e8c0f62..b25d15af5 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -51,7 +51,7 @@ const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
 const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
 const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
 const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
-const std::string CUSTOM_GCODE_PER_HEIGHT_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_height.xml";
+const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
 
 const char* MODEL_TAG = "model";
 const char* RESOURCES_TAG = "resources";
@@ -418,7 +418,7 @@ namespace Slic3r {
         void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
         void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
 
-        void _extract_custom_gcode_per_height_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
+        void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
 
         void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename);
         bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
@@ -629,10 +629,10 @@ namespace Slic3r {
                     // extract slic3r print config file
                     _extract_print_config_from_archive(archive, stat, config, filename);
                 }
-                if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_HEIGHT_FILE))
+                if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE))
                 {
                     // extract slic3r layer config ranges file
-                    _extract_custom_gcode_per_height_from_archive(archive, stat);
+                    _extract_custom_gcode_per_print_z_from_archive(archive, stat);
                 }
                 else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE))
                 {
@@ -1064,7 +1064,7 @@ namespace Slic3r {
         return true;
     }
 
-    void _3MF_Importer::_extract_custom_gcode_per_height_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
+    void _3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
     {
         if (stat.m_uncomp_size > 0)
         {
@@ -1079,24 +1079,23 @@ namespace Slic3r {
             pt::ptree main_tree;
             pt::read_xml(iss, main_tree);
 
-            if (main_tree.front().first != "custom_gcodes_per_height")
+            if (main_tree.front().first != "custom_gcodes_per_print_z")
                 return;
             pt::ptree code_tree = main_tree.front().second;
 
-            if (!m_model->custom_gcode_per_height.empty())
-                m_model->custom_gcode_per_height.clear();
+            m_model->custom_gcode_per_print_z.clear();
 
             for (const auto& code : code_tree)
             {
                 if (code.first != "code")
                     continue;
                 pt::ptree tree = code.second;
-                double height       = tree.get<double>("<xmlattr>.height");
-                std::string gcode   = tree.get<std::string>("<xmlattr>.gcode");
-                int extruder        = tree.get<int>("<xmlattr>.extruder");
-                std::string color   = tree.get<std::string>("<xmlattr>.color");
+                double print_z      = tree.get<double>      ("<xmlattr>.print_z"    );
+                std::string gcode   = tree.get<std::string> ("<xmlattr>.gcode"      );
+                int extruder        = tree.get<int>         ("<xmlattr>.extruder"   );
+                std::string color   = tree.get<std::string> ("<xmlattr>.color"      );
 
-                m_model->custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)) ;
+                m_model->custom_gcode_per_print_z.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
             }
         }
     }
@@ -1885,7 +1884,7 @@ namespace Slic3r {
         bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
         bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
         bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
-        bool _add_custom_gcode_per_height_file_to_archive(mz_zip_archive& archive, Model& model);
+        bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model);
     };
 
 #if ENABLE_THUMBNAIL_GENERATOR
@@ -1988,9 +1987,9 @@ namespace Slic3r {
             return false;
         }
 
-        // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_height.xml").
+        // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
         // All custom gcode per height of whole Model are stored here
-        if (!_add_custom_gcode_per_height_file_to_archive(archive, model))
+        if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model))
         {
             close_zip_writer(&archive);
             boost::filesystem::remove(filename);
@@ -2567,20 +2566,20 @@ namespace Slic3r {
         return true;
     }
 
-bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive& archive, Model& model)
+bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model)
 {
     std::string out = "";
 
-    if (!model.custom_gcode_per_height.empty())
+    if (!model.custom_gcode_per_print_z.empty())
     {
         pt::ptree tree;
-        pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
+        pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
 
-        for (const Model::CustomGCode& code : model.custom_gcode_per_height)
+        for (const Model::CustomGCode& code : model.custom_gcode_per_print_z)
         {
             pt::ptree& code_tree = main_tree.add("code", "");
             // store minX and maxZ
-            code_tree.put("<xmlattr>.height"    , code.height   );
+            code_tree.put("<xmlattr>.print_z"   , code.print_z  );
             code_tree.put("<xmlattr>.gcode"     , code.gcode    );
             code_tree.put("<xmlattr>.extruder"  , code.extruder );
             code_tree.put("<xmlattr>.color"     , code.color    );
@@ -2599,9 +2598,9 @@ bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive
 
     if (!out.empty())
     {
-        if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_HEIGHT_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
+        if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
         {
-            add_error("Unable to add custom Gcodes per height file to archive");
+            add_error("Unable to add custom Gcodes per print_z file to archive");
             return false;
         }
     }
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index d50f6e395..aee8b7401 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -637,7 +637,7 @@ void AMFParserContext::endElement(const char * /* name */)
         int extruder = atoi(m_value[2].c_str());
         const std::string& color = m_value[3];
 
-        m_model.custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color));
+        m_model.custom_gcode_per_print_z.push_back(Model::CustomGCode{height, gcode, extruder, color});
 
         for (std::string& val: m_value)
             val.clear();
@@ -1221,21 +1221,21 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
         stream << "  </constellation>\n";
     }
 
-    if (!model->custom_gcode_per_height.empty())
+    if (!model->custom_gcode_per_print_z.empty())
     {
         std::string out = "";
         pt::ptree tree;
 
         pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
 
-        for (const Model::CustomGCode& code : model->custom_gcode_per_height)
+        for (const Model::CustomGCode& code : model->custom_gcode_per_print_z)
         {
             pt::ptree& code_tree = main_tree.add("code", "");
             // store minX and maxZ
-            code_tree.put("<xmlattr>.height", code.height);
-            code_tree.put("<xmlattr>.gcode", code.gcode);
-            code_tree.put("<xmlattr>.extruder", code.extruder);
-            code_tree.put("<xmlattr>.color", code.color);
+            code_tree.put("<xmlattr>.print_z"   , code.print_z  );
+            code_tree.put("<xmlattr>.gcode"     , code.gcode    );
+            code_tree.put("<xmlattr>.extruder"  , code.extruder );
+            code_tree.put("<xmlattr>.color"     , code.color    );
         }
 
         if (!tree.empty())
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 91627631f..4d18a3298 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -936,7 +936,7 @@ void GCode::_do_export(Print& print, FILE* file)
 
     // Initialize custom gcode
     Model* model = print.get_object(0)->model_object()->get_model();
-    m_custom_g_code_heights = model->custom_gcode_per_height;
+    m_custom_gcode_per_print_z = model->custom_gcode_per_print_z;
 
     // Initialize autospeed.
     {
@@ -1125,19 +1125,19 @@ void GCode::_do_export(Print& print, FILE* file)
     print.throw_if_canceled();
 
     /* To avoid change filament for non-used extruder for Multi-material,
-     * check model->custom_gcode_per_height using tool_ordering values
+     * check model->custom_gcode_per_print_z using tool_ordering values
      * */
-    if (!m_custom_g_code_heights. empty())
+    if (!m_custom_gcode_per_print_z. empty())
     {
         bool delete_executed = false;
-        auto it = m_custom_g_code_heights.end();
-        while (it != m_custom_g_code_heights.begin())
+        auto it = m_custom_gcode_per_print_z.end();
+        while (it != m_custom_gcode_per_print_z.begin())
         {
             --it;
             if (it->gcode != ColorChangeCode)
                 continue;
 
-            auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->height));
+            auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->print_z));
 
             bool used_extruder = false;
             for (; it_layer_tools != tool_ordering.end(); it_layer_tools++)
@@ -1154,14 +1154,14 @@ void GCode::_do_export(Print& print, FILE* file)
 
             /* If we are there, current extruder wouldn't be used,
              * so this color change is a redundant move.
-             * Delete this item from m_custom_g_code_heights
+             * Delete this item from m_custom_gcode_per_print_z
              * */
-            it = m_custom_g_code_heights.erase(it);
+            it = m_custom_gcode_per_print_z.erase(it);
             delete_executed = true;
         }
 
         if (delete_executed)
-            model->custom_gcode_per_height = m_custom_g_code_heights;
+            model->custom_gcode_per_print_z = m_custom_gcode_per_print_z;
     }
 
 
@@ -1461,7 +1461,7 @@ void GCode::_do_export(Print& print, FILE* file)
                 dst.first += buf;
                 ++ dst.second;
             };
-            print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
+            print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>{extruder.id(), (float)used_filament});
             append(out_filament_used_mm,  "%.1lf", used_filament);
             append(out_filament_used_cm3, "%.1lf", extruded_volume * 0.001);
             if (filament_weight > 0.) {
@@ -1835,15 +1835,15 @@ void GCode::process_layer(
     std::string custom_code = "";
     std::string pause_print_msg = "";
     int m600_before_extruder = -1;
-    while (!m_custom_g_code_heights.empty() && m_custom_g_code_heights.front().height-EPSILON < layer.print_z) {
-        custom_code = m_custom_g_code_heights.front().gcode;
+    while (!m_custom_gcode_per_print_z.empty() && m_custom_gcode_per_print_z.front().print_z - EPSILON < layer.print_z) {
+        custom_code = m_custom_gcode_per_print_z.front().gcode;
 
-        if (custom_code == ColorChangeCode && m_custom_g_code_heights.front().extruder > 0)
-            m600_before_extruder = m_custom_g_code_heights.front().extruder - 1;
+        if (custom_code == ColorChangeCode && m_custom_gcode_per_print_z.front().extruder > 0)
+            m600_before_extruder = m_custom_gcode_per_print_z.front().extruder - 1;
         if (custom_code == PausePrintCode)
-            pause_print_msg = m_custom_g_code_heights.front().color;
+            pause_print_msg = m_custom_gcode_per_print_z.front().color;
 
-        m_custom_g_code_heights.erase(m_custom_g_code_heights.begin());
+        m_custom_gcode_per_print_z.erase(m_custom_gcode_per_print_z.begin());
         colorprint_change = true;
     }
 
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 7f009b814..68bb85a98 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -367,7 +367,7 @@ protected:
      * Updated before the export and erased during the process,
      * so no toolchange occurs twice.
      * */
-    std::vector<Model::CustomGCode> m_custom_g_code_heights;
+    std::vector<Model::CustomGCode> m_custom_gcode_per_print_z;
 
     // Time estimators
     GCodeTimeEstimator m_normal_time_estimator;
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index 664b0e3a1..98abdda28 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -10,6 +10,11 @@
 
 namespace Slic3r {
 
+// Additional Codes which can be set by user using DoubleSlider
+static constexpr char ColorChangeCode[]     = "M600";
+static constexpr char PausePrintCode[]      = "M601";
+static constexpr char ExtruderChangeCode[]  = "tool_change";
+
 class GCodeWriter {
 public:
     GCodeConfig config;
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index ce3debfb5..5a6beb851 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -18,6 +18,7 @@
 
 #include "SVG.hpp"
 #include <Eigen/Dense>
+#include "GCodeWriter.hpp"
 
 namespace Slic3r {
 
@@ -43,7 +44,7 @@ Model& Model::assign_copy(const Model &rhs)
     }
 
     // copy custom code per height
-    this->custom_gcode_per_height = rhs.custom_gcode_per_height;
+    this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
     return *this;
 }
 
@@ -64,7 +65,7 @@ Model& Model::assign_copy(Model &&rhs)
     rhs.objects.clear();
 
     // copy custom code per height
-    this->custom_gcode_per_height = rhs.custom_gcode_per_height;
+    this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
     return *this;
 }
 
@@ -595,16 +596,15 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
 std::vector<std::pair<double, DynamicPrintConfig>> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const
 {
     std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes;
-    if (!custom_gcode_per_height.empty()) {
-        for (const CustomGCode& custom_gcode : custom_gcode_per_height)
-            if (custom_gcode.gcode == ExtruderChangeCode) {
-                DynamicPrintConfig config;
-                // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
-                config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder));
-                // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height
-                custom_tool_changes.push_back({ custom_gcode.height - default_layer_height, config });
-            }
-    }
+    for (const CustomGCode& custom_gcode : custom_gcode_per_print_z)
+        if (custom_gcode.gcode == ExtruderChangeCode) {
+            DynamicPrintConfig config;
+            // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
+            config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder));
+            // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height
+            custom_tool_changes.push_back({ custom_gcode.print_z - default_layer_height, config });
+        }
+
     return custom_tool_changes;
 }
 
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index c2942a4ea..36ad31175 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -749,33 +749,30 @@ public:
     // Extensions for color print
     struct CustomGCode
     {
-        CustomGCode(double height, const std::string& code, int extruder, const std::string& color) :
-            height(height), gcode(code), extruder(extruder), color(color) {}
-
-        bool operator<(const CustomGCode& other) const { return other.height > this->height; }
+        bool operator<(const CustomGCode& other) const { return other.print_z > this->print_z; }
         bool operator==(const CustomGCode& other) const
         {
-            return (other.height    == this->height)     && 
-                   (other.gcode     == this->gcode)      && 
-                   (other.extruder  == this->extruder   )&& 
-                   (other.color     == this->color   );
+            return (other.print_z   == this->print_z    ) && 
+                   (other.gcode     == this->gcode      ) && 
+                   (other.extruder  == this->extruder   ) && 
+                   (other.color     == this->color      );
         }
         bool operator!=(const CustomGCode& other) const
         {
-            return (other.height    != this->height)     || 
-                   (other.gcode     != this->gcode)      || 
-                   (other.extruder  != this->extruder   )|| 
-                   (other.color     != this->color   );
+            return (other.print_z   != this->print_z    ) || 
+                   (other.gcode     != this->gcode      ) || 
+                   (other.extruder  != this->extruder   ) || 
+                   (other.color     != this->color      );
         }
         
-        double      height;
+        double      print_z;
         std::string gcode;
         int         extruder;   // 0    - "gcode" will be applied for whole print
                                 // else - "gcode" will be applied only for "extruder" print
         std::string color;      // if gcode is equal to PausePrintCode, 
                                 // this field is used for save a short message shown on Printer display 
     };
-    std::vector<CustomGCode> custom_gcode_per_height;
+    std::vector<CustomGCode> custom_gcode_per_print_z;
     
     // Default constructor assigns a new ID to the model.
     Model() { assert(this->id().valid()); }
@@ -841,7 +838,7 @@ public:
     // Propose an output path, replace extension. The new_extension shall contain the initial dot.
     std::string   propose_export_file_name_and_path(const std::string &new_extension) const;
 
-    // from custom_gcode_per_height get just tool_change codes
+    // from custom_gcode_per_print_z get just tool_change codes
     std::vector<std::pair<double, DynamicPrintConfig>> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const;
 
 private:
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index ae32bd2d3..1d4e69763 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -739,10 +739,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
 				model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
 
             // But if custom gcode per layer height was changed
-            if (m_model.custom_gcode_per_height != model.custom_gcode_per_height) {
+            if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
                 // we should stop background processing
                 update_apply_status(this->invalidate_step(psGCodeExport));
-                m_model.custom_gcode_per_height = model.custom_gcode_per_height;
+                m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
             }
         } else if (model_object_list_extended(m_model, model)) {
             // Add new objects. Their volumes and configs will be synchronized later.
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 9f3bc0539..5f5a861da 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -71,12 +71,6 @@ enum SLAPillarConnectionMode {
     slapcmDynamic
 };
 
-// ys_FIXME ! may be, it's not a best place
-// Additional Codes which can be set by user using DoubleSlider
-static const std::string ColorChangeCode    = "M600";
-static const std::string PausePrintCode     = "M601";
-static const std::string ExtruderChangeCode = "tool_change";
-
 template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index e587509ac..b26c42841 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -94,8 +94,8 @@ void BackgroundSlicingProcess::process_fff()
     m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
 #endif // ENABLE_THUMBNAIL_GENERATOR
 
-    if (m_fff_print->model().custom_gcode_per_height != GUI::wxGetApp().model().custom_gcode_per_height) {
-        GUI::wxGetApp().model().custom_gcode_per_height = m_fff_print->model().custom_gcode_per_height;
+    if (m_fff_print->model().custom_gcode_per_print_z != GUI::wxGetApp().model().custom_gcode_per_print_z) {
+        GUI::wxGetApp().model().custom_gcode_per_print_z = m_fff_print->model().custom_gcode_per_print_z;
         // #ys_FIXME : controll text
         GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n"
                                     "Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info")));
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 8e922398a..a06754c01 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1011,24 +1011,25 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items(  const GLCanvas3D
                                                                 std::vector<float>& colors,
                                                                 std::vector<std::string>& cp_legend_items)
 {
-    std::vector<Model::CustomGCode> custom_gcode_per_height = wxGetApp().plater()->model().custom_gcode_per_height;
+    std::vector<Model::CustomGCode> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z;
 
     const int extruders_cnt = wxGetApp().extruders_edited_cnt();
     if (extruders_cnt == 1) 
     {
-        if (custom_gcode_per_height.empty()) {
-            cp_legend_items.push_back(I18N::translate_utf8(L("Default print color")));
+        if (custom_gcode_per_print_z.empty()) {
+            cp_legend_items.emplace_back(I18N::translate_utf8(L("Default print color")));
             colors = colors_in;
             return;
         }
         std::vector<std::pair<double, double>> cp_values;
+        cp_values.reserve(custom_gcode_per_print_z.size());
         
         std::vector<double> print_zs = canvas.get_current_print_zs(true);
-        for (auto custom_code : custom_gcode_per_height)
+        for (auto custom_code : custom_gcode_per_print_z)
         {
             if (custom_code.gcode != ColorChangeCode)
                 continue;
-            auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.height - DoubleSlider::epsilon());
+            auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon());
 
             if (lower_b == print_zs.end())
                 continue;
@@ -1039,14 +1040,14 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items(  const GLCanvas3D
             // to avoid duplicate values, check adding values
             if (cp_values.empty() ||
                 !(cp_values.back().first == previous_z && cp_values.back().second == current_z))
-                cp_values.push_back(std::pair<double, double>(previous_z, current_z));
+                cp_values.emplace_back(std::pair<double, double>(previous_z, current_z));
         }
 
         const auto items_cnt = (int)cp_values.size();
         if (items_cnt == 0) // There is no one color change, but there is/are some pause print or custom Gcode
         {
-            cp_legend_items.push_back(I18N::translate_utf8(L("Default print color")));
-            cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
+            cp_legend_items.emplace_back(I18N::translate_utf8(L("Default print color")));
+            cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code")));
             colors = colors_in;
             return;
         }
@@ -1055,7 +1056,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items(  const GLCanvas3D
         colors.resize(colors_in.size(), 0.0);
                 
         ::memcpy((void*)(colors.data()), (const void*)(colors_in.data() + (color_cnt - 1) * 4), 4 * sizeof(float));
-        cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
+        cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code")));
         size_t color_pos = 4;
 
         for (int i = items_cnt; i >= 0; --i, color_pos+=4)
@@ -1067,15 +1068,15 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items(  const GLCanvas3D
             std::string id_str = std::to_string(i + 1) + ": ";
 
             if (i == 0) {
-                cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str());
+                cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str());
                 break;
             }
             if (i == items_cnt) {
-                cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str());
+                cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str());
                 continue;
             }
 
-            cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str());
+            cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str());
         }
     }
     else
@@ -1089,20 +1090,20 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items(  const GLCanvas3D
         size_t color_in_pos = 4 * (color_cnt - 1);
         
         for (unsigned int i = 0; i < (unsigned int)extruders_cnt; ++i)
-            cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str());
+            cp_legend_items.emplace_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str());
 
         ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float));
         color_pos += 4;
         color_in_pos -= 4;
-        cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
+        cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code")));
 
-        int cnt = custom_gcode_per_height.size();
+        int cnt = custom_gcode_per_print_z.size();
         for (int i = cnt-1; i >= 0; --i)
-            if (custom_gcode_per_height[i].gcode == ColorChangeCode) {
+            if (custom_gcode_per_print_z[i].gcode == ColorChangeCode) {
                 ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float));
                 color_pos += 4;
                 color_in_pos -= 4;
-                cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_height[i].extruder % custom_gcode_per_height[i].height).str());
+                cp_legend_items.emplace_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str());
             }
     }
 }
@@ -5385,7 +5386,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
         // For coloring by a color_print(M600), return a parsed color.
         bool                         color_by_color_print() const { return color_print_values!=nullptr; }
         const size_t                 color_print_color_idx_by_layer_idx(const size_t layer_idx) const {
-            const Model::CustomGCode value(layers[layer_idx]->print_z + EPSILON, "", 0, "");
+            const Model::CustomGCode value{layers[layer_idx]->print_z + EPSILON, "", 0, ""};
             auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
             return (it - color_print_values->begin()) % number_tools();
         }
@@ -5396,7 +5397,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
 
             auto it = std::find_if(color_print_values->begin(), color_print_values->end(),
                 [print_z](const Model::CustomGCode& code)
-                { return fabs(code.height - print_z) < EPSILON; });
+                { return fabs(code.print_z - print_z) < EPSILON; });
             if (it != color_print_values->end())
             {
                 const std::string& code = it->gcode;
@@ -5416,7 +5417,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
                 }
             }
 
-            const Model::CustomGCode value(print_z + EPSILON, "", 0, "");
+            const Model::CustomGCode value{print_z + EPSILON, "", 0, ""};
             it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
             while (it != color_print_values->begin())
             {
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index edb244b34..2eb316be0 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -569,7 +569,7 @@ void Preview::update_view_type(bool slice_completed)
 {
     const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
 
-    const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_height.empty() /*&&
+    const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.empty() /*&&
                              (wxGetApp().extruders_edited_cnt()==1 || !slice_completed) */? 
                                 _(L("Color Print")) :
                                 config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
@@ -600,7 +600,7 @@ void Preview::create_double_slider()
 
     Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
         Model& model = wxGetApp().plater()->model();
-        model.custom_gcode_per_height = m_slider->GetTicksValues();
+        model.custom_gcode_per_print_z = m_slider->GetTicksValues();
         m_schedule_background_process();
 
         update_view_type(false);
@@ -646,7 +646,7 @@ void Preview::check_slider_values(std::vector<Model::CustomGCode>& ticks_from_mo
     ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(),
                      [layers_z](Model::CustomGCode val)
         {
-            auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.height - DoubleSlider::epsilon());
+            auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon());
             return it == layers_z.end();
         }),
         ticks_from_model.end());
@@ -669,7 +669,7 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee
     bool   snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min();
 	bool   snap_to_max  = force_sliders_full_range || m_slider->is_higher_at_max();
 
-    std::vector<Model::CustomGCode> &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_height;
+    std::vector<Model::CustomGCode> &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z;
     check_slider_values(ticks_from_model, layers_z);
 
     m_slider->SetSliderValues(layers_z);
@@ -789,7 +789,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
         colors.push_back("#808080"); // gray color for pause print or custom G-code 
 
         if (!gcode_preview_data_valid)
-            color_print_values = wxGetApp().plater()->model().custom_gcode_per_height;
+            color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z;
     }
     else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool) )
     {
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 861ff47da..ef891b3b3 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -2334,7 +2334,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
                         config += std::move(config_loaded);
                     }
 
-                    this->model.custom_gcode_per_height = model.custom_gcode_per_height;
+                    this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
                 }
 
                 if (load_config)
@@ -2753,7 +2753,7 @@ void Plater::priv::reset()
     // The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here
     this->sidebar->show_sliced_info_sizer(false);
 
-    model.custom_gcode_per_height.clear();
+    model.custom_gcode_per_print_z.clear();
 }
 
 void Plater::priv::mirror(Axis axis)
@@ -5174,6 +5174,7 @@ const DynamicPrintConfig* Plater::get_plater_config() const
     return p->config;
 }
 
+// Get vector of extruder colors considering filament color, if extruder color is undefined.
 std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const
 {
     const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
@@ -5193,13 +5194,17 @@ std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const
     return extruder_colors;
 }
 
+/* Get vector of colors used for rendering of a Preview scene in "Color print" mode
+ * It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z
+ */
 std::vector<std::string> Plater::get_colors_for_color_print() const
 {
     std::vector<std::string> colors = get_extruder_colors_from_plater_config();
+    colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.size());
 
-    for (const Model::CustomGCode& code : p->model.custom_gcode_per_height)
+    for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z)
         if (code.gcode == ColorChangeCode)
-            colors.push_back(code.color);
+            colors.emplace_back(code.color);
 
     return colors;
 }
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index 9b8eaa8ec..56752ab77 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -29,6 +29,7 @@
 
 #include "libslic3r/libslic3r.h"
 #include "libslic3r/Utils.hpp"
+#include "GUI_App.hpp"
 
 // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir.
 // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions.
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index 302a5e521..71da828f7 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -2538,7 +2538,7 @@ std::vector<t_custom_code> DoubleSlider::GetTicksValues() const
         for (const TICK_CODE& tick : m_ticks_) {
             if (tick.tick > val_size)
                 break;
-            values.push_back(t_custom_code(m_values[tick.tick], tick.gcode, tick.extruder, tick.color));
+            values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color});
         }
 
     return values;
@@ -2553,12 +2553,12 @@ void DoubleSlider::SetTicksValues(const std::vector<t_custom_code>& heights)
 
     m_ticks_.clear();
     for (auto h : heights) {
-        auto it = std::lower_bound(m_values.begin(), m_values.end(), h.height - epsilon());
+        auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon());
 
         if (it == m_values.end())
             continue;
 
-        m_ticks_.insert(TICK_CODE(it-m_values.begin(), h.gcode, h.extruder, h.color));
+        m_ticks_.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color});
     }
     
     if (!was_empty && m_ticks_.empty())
@@ -2642,7 +2642,7 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin
         return;
 
     wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp();
-    if (m_ticks_.find(tick) != m_ticks_.end())
+    if (m_ticks_.find(TICK_CODE{tick}) != m_ticks_.end())
         icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp();
 
     wxCoord x_draw, y_draw;
@@ -3081,7 +3081,7 @@ wxString DoubleSlider::get_tooltip(IconFocus icon_focus)
     else if (m_is_action_icon_focesed)
     {
         const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
-        const auto tick_code_it = m_ticks_.find(tick);
+        const auto tick_code_it = m_ticks_.find(TICK_CODE{tick});
         tooltip = tick_code_it == m_ticks_.end()            ? (m_state == msSingleExtruder ?
                         _(L("For add color change use left mouse button click")) :
                         _(L("For add change extruder use left mouse button click"))) + "\n" +
@@ -3240,13 +3240,13 @@ void DoubleSlider::action_tick(const TicksAction action)
 
     const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
 
-    const auto it = m_ticks_.find(tick);
+    const auto it = m_ticks_.find(TICK_CODE{tick});
 
     if (it != m_ticks_.end()) // erase this tick
     {
         if (action == taAdd)
             return;
-        m_ticks_.erase(TICK_CODE(tick));
+        m_ticks_.erase(TICK_CODE{tick});
 
         wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
         Refresh();
@@ -3350,7 +3350,7 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event)
     {
         const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
         // if on this Z doesn't exist tick
-        auto it = m_ticks_.find(tick);
+        auto it = m_ticks_.find(TICK_CODE{ tick });
         if (it == m_ticks_.end())
         {
             // show context menu on OnRightUp()
@@ -3387,7 +3387,7 @@ int DoubleSlider::get_extruder_for_tick(int tick)
     if (m_ticks_.empty())
         return 0;
     
-    auto it = m_ticks_.lower_bound(tick);
+    auto it = m_ticks_.lower_bound(TICK_CODE{tick});
     while (it != m_ticks_.begin()) {
         --it;
         if(it->gcode == Slic3r::ExtruderChangeCode)
@@ -3454,7 +3454,7 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event)
     else if (m_show_edit_menu) {
         wxMenu menu;
 
-        std::set<TICK_CODE>::iterator it = m_ticks_.find(m_selection == ssLower ? m_lower_value : m_higher_value);
+        std::set<TICK_CODE>::iterator it = m_ticks_.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value });
         const bool is_color_change = it->gcode == Slic3r::ColorChangeCode;
 
         append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) :
@@ -3526,7 +3526,7 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/)
 {
     const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
     // if on this Z doesn't exist tick
-    auto it = m_ticks_.find(tick);
+    auto it = m_ticks_.find(TICK_CODE{ tick });
     if (it == m_ticks_.end())
     {
         std::string color = "";
@@ -3535,7 +3535,7 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/)
             std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
 
             if (m_state == msSingleExtruder && !m_ticks_.empty()) {
-                auto before_tick_it = std::lower_bound(m_ticks_.begin(), m_ticks_.end(), tick);
+                auto before_tick_it = std::lower_bound(m_ticks_.begin(), m_ticks_.end(), TICK_CODE{ tick });
                 while (before_tick_it != m_ticks_.begin()) {
                     --before_tick_it;
                     if (before_tick_it->gcode == Slic3r::ColorChangeCode) {
@@ -3580,7 +3580,7 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/)
                 extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value);
         }
 
-        m_ticks_.insert(TICK_CODE(tick, code, extruder, color));
+        m_ticks_.emplace(TICK_CODE{tick, code, extruder, color});
 
         wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
         Refresh();
@@ -3592,7 +3592,7 @@ void DoubleSlider::edit_tick()
 {
     const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
     // if on this Z exists tick
-    std::set<TICK_CODE>::iterator it = m_ticks_.find(tick);
+    std::set<TICK_CODE>::iterator it = m_ticks_.find(TICK_CODE{ tick });
     if (it != m_ticks_.end())
     {
         std::string edited_value;
@@ -3619,7 +3619,7 @@ void DoubleSlider::edit_tick()
         }
         
         m_ticks_.erase(it);
-        m_ticks_.insert(changed_tick);
+        m_ticks_.emplace(changed_tick);
 
         wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
     }
@@ -3632,9 +3632,9 @@ void DoubleSlider::change_extruder(int extruder)
     std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
 
     // if on this Y doesn't exist tick
-    if (m_ticks_.find(tick) == m_ticks_.end())
+    if (m_ticks_.find(TICK_CODE{tick}) == m_ticks_.end())
     {        
-        m_ticks_.insert(TICK_CODE(tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1]));
+        m_ticks_.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1]});
 
         wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
         Refresh();
@@ -3672,7 +3672,7 @@ void DoubleSlider::edit_extruder_sequence()
     while (tick <= m_max_value)
     {
         int cur_extruder = m_extruders_sequence.extruders[extruder];
-        m_ticks_.insert(TICK_CODE(tick, Slic3r::ExtruderChangeCode, cur_extruder + 1, colors[cur_extruder]));
+        m_ticks_.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, cur_extruder + 1, colors[cur_extruder]});
 
         extruder++;
         if (extruder == extr_cnt)
@@ -3680,12 +3680,12 @@ void DoubleSlider::edit_extruder_sequence()
         if (m_extruders_sequence.is_mm_intervals)
         {
             value += m_extruders_sequence.interval_by_mm;
-            auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon());
+            auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon());
 
-            if (it == m_values.end())
+            if (val_it == m_values.end())
                 break;
 
-            tick = it - m_values.begin();
+            tick = val_it - m_values.begin();
         }
         else
             tick += m_extruders_sequence.interval_by_layers;
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 7841b62fe..9b7de93cd 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -17,6 +17,7 @@
 #include <set>
 #include <functional>
 #include "libslic3r/Model.hpp"
+#include "libslic3r/GCodeWriter.hpp"
 
 namespace Slic3r {
     enum class ModelVolumeType : int;
@@ -958,24 +959,12 @@ private:
 
     struct TICK_CODE
     {
-        TICK_CODE(int tick):tick(tick), gcode(Slic3r::ColorChangeCode), extruder(0), color("") {}
-        TICK_CODE(int tick, const std::string& code) : 
-                            tick(tick), gcode(code), extruder(0) {}
-        TICK_CODE(int tick, int extruder) :
-                            tick(tick), gcode(Slic3r::ColorChangeCode), extruder(extruder) {}
-        TICK_CODE(int tick, const std::string& code, int extruder, const std::string& color) : 
-                            tick(tick), gcode(code), extruder(extruder), color(color) {}
-
         bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
         bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
-        TICK_CODE operator=(const TICK_CODE& other) const {
-            TICK_CODE ret_val(other.tick, other.gcode, other.extruder, other.color);
-            return ret_val;
-        }
 
         int         tick;
-        std::string gcode;
-        int         extruder;
+        std::string gcode = Slic3r::ColorChangeCode;
+        int         extruder = 0;
         std::string color;
     };
 

From 20e59cb0c78835355494c61223a437f20fbbf452 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 17 Dec 2019 10:51:18 +0100
Subject: [PATCH 09/17] Fix missing gmpxx from dep_GMP

---
 deps/GMP/GMP.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deps/GMP/GMP.cmake b/deps/GMP/GMP.cmake
index 6c93107c4..8bcf94859 100644
--- a/deps/GMP/GMP.cmake
+++ b/deps/GMP/GMP.cmake
@@ -20,7 +20,7 @@ else ()
     ExternalProject_Add(dep_GMP
         URL  https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2
         BUILD_IN_SOURCE ON 
-        CONFIGURE_COMMAND ./configure --enable-shared=no --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic
+        CONFIGURE_COMMAND ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic
         BUILD_COMMAND     make -j
         INSTALL_COMMAND   make install
     )

From aff5adaaa06dbbb2d6b0f9641a3ae54e275a2124 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 17 Dec 2019 10:55:08 +0100
Subject: [PATCH 10/17] Fixed a typo in extrusion ordering, pointed by
 @supermerill

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

diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 39b82f4ce..e9df4c5b5 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -1000,13 +1000,13 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
 	auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
 	auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
 	std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
-	for (size_t i = 0; i < entities.size(); ++ i) {
-		ExtrusionEntity *ee = entities[i];
+	for (std::pair<size_t, bool> &segment : out) {
+		ExtrusionEntity *ee = entities[segment.first];
 		if (ee->is_loop())
 			// Ignore reversals for loops, as the start point equals the end point.
-			out[i].second = false;
+			segment.second = false;
 		// Is can_reverse() respected by the reversals?
-		assert(entities[i]->can_reverse() || ! out[i].second);
+		assert(ee->can_reverse() || ! segment.second);
 	}
 	return out;
 }

From c3214e9f8656bf4e1cc4ab53b04a75352b2bf78c Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 17 Dec 2019 13:05:18 +0100
Subject: [PATCH 11/17] Fix CGAL config script being non-relocatable

---
 deps/CGAL/CGAL.cmake | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/deps/CGAL/CGAL.cmake b/deps/CGAL/CGAL.cmake
index 4b127cd51..96a629258 100644
--- a/deps/CGAL/CGAL.cmake
+++ b/deps/CGAL/CGAL.cmake
@@ -6,4 +6,10 @@ prusaslicer_add_cmake_project(
     # URL      https://github.com/CGAL/cgal/archive/releases/CGAL-5.0.zip
     # URL_HASH SHA256=bd9327be903ab7ee379a8a7a0609eba0962f5078d2497cf8e13e8e1598584154
     DEPENDS dep_boost dep_GMP dep_MPFR
+)
+
+ExternalProject_Add_Step(dep_CGAL dep_CGAL_relocation_fix
+    DEPENDEES install
+    COMMAND ${CMAKE_COMMAND} -E remove CGALConfig-installation-dirs.cmake
+    WORKING_DIRECTORY "${DESTDIR}/usr/local/lib/cmake/CGAL"
 )
\ No newline at end of file

From 8824468882e4fe24a3503424efe3e7b0e7ca8893 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 17 Dec 2019 13:16:28 +0100
Subject: [PATCH 12/17] Added function to update of custom_gcode_per_print_z in
 Model from configuration considering "colorprint_heights" option.

Changed thumb_up/down icons to better preview (feedback from #3256)

Commented some uncertain code
---
 resources/icons/thumb_down.svg              |  2 +-
 resources/icons/thumb_up.svg                |  2 +-
 src/libslic3r/GCode.cpp                     |  8 +++---
 src/libslic3r/Model.cpp                     | 29 +++++++++++++++++++++
 src/libslic3r/Model.hpp                     |  4 +++
 src/slic3r/GUI/BackgroundSlicingProcess.cpp |  3 ++-
 src/slic3r/GUI/PresetBundle.cpp             |  3 +++
 7 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/resources/icons/thumb_down.svg b/resources/icons/thumb_down.svg
index 0499cea41..f789b7317 100644
--- a/resources/icons/thumb_down.svg
+++ b/resources/icons/thumb_down.svg
@@ -4,7 +4,7 @@
 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
 <g id="hex_x5F_plus">
 	<g>
-		<polygon fill="#ED6B21" points="2,8 2,11 8,15 14,11 14,8 "/>
+		<polygon fill="#ED6B21" points="1,8 1,11 8,16 15,11 15,8 " style="stroke:white;stroke-width:1"/>
 	</g>
 </g>
 </svg>
diff --git a/resources/icons/thumb_up.svg b/resources/icons/thumb_up.svg
index c9045929b..1a0c6f1b7 100644
--- a/resources/icons/thumb_up.svg
+++ b/resources/icons/thumb_up.svg
@@ -4,7 +4,7 @@
 	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
 <g id="hex_x5F_plus">
 	<g>
-		<polygon fill="#ED6B21" points="8,1 2,5 2,7 2,8 14,8 14,7 14,5 		"/>
+		<polygon fill="#ED6B21" points="8,0 1,5 1,7 1,8 15,8 15,7 15,5" style="stroke:white;stroke-width:1"/>
 	</g>
 </g>
 </svg>
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 4d18a3298..433422d99 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1124,9 +1124,11 @@ void GCode::_do_export(Print& print, FILE* file)
     }
     print.throw_if_canceled();
 
+// #ys_FIXME_no_exported_codes
+    /*
     /* To avoid change filament for non-used extruder for Multi-material,
      * check model->custom_gcode_per_print_z using tool_ordering values
-     * */
+     * /
     if (!m_custom_gcode_per_print_z. empty())
     {
         bool delete_executed = false;
@@ -1155,7 +1157,7 @@ void GCode::_do_export(Print& print, FILE* file)
             /* If we are there, current extruder wouldn't be used,
              * so this color change is a redundant move.
              * Delete this item from m_custom_gcode_per_print_z
-             * */
+             * /
             it = m_custom_gcode_per_print_z.erase(it);
             delete_executed = true;
         }
@@ -1163,7 +1165,7 @@ void GCode::_do_export(Print& print, FILE* file)
         if (delete_executed)
             model->custom_gcode_per_print_z = m_custom_gcode_per_print_z;
     }
-
+*/
 
     m_cooling_buffer->set_current_extruder(initial_extruder_id);
 
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 5a6beb851..7137527e9 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -19,6 +19,7 @@
 #include "SVG.hpp"
 #include <Eigen/Dense>
 #include "GCodeWriter.hpp"
+#include "GCode/PreviewData.hpp"
 
 namespace Slic3r {
 
@@ -125,6 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
     if (add_default_instances)
         model.add_default_instances();
 
+    update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
+
     return model;
 }
 
@@ -160,6 +163,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
     if (add_default_instances)
         model.add_default_instances();
 
+    update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
+
     return model;
 }
 
@@ -1933,6 +1938,30 @@ extern bool model_has_advanced_features(const Model &model)
     return false;
 }
 
+extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config)
+{
+    if (!config->has("colorprint_heights"))
+        return;
+
+    const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
+
+    const auto& colorprint_values = config->option<ConfigOptionFloats>("colorprint_heights")->values;
+    
+    if (!colorprint_values.empty())
+    {
+        custom_gcode_per_print_z.clear();
+        custom_gcode_per_print_z.reserve(colorprint_values.size());
+        int i = 0;
+        for (auto val : colorprint_values)
+            custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] });
+    }
+
+    /* There is one and only place this configuration option is used now.
+     * It wouldn't be used in the future, so erase it.
+     * */
+    config->erase("colorprint_heights");
+}
+
 #ifndef NDEBUG
 // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
 void check_model_ids_validity(const Model &model)
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 36ad31175..9f173f6ea 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -874,6 +874,10 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const
 extern bool model_has_multi_part_objects(const Model &model);
 // If the model has advanced features, then it cannot be processed in simple mode.
 extern bool model_has_advanced_features(const Model &model);
+/* If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer), 
+ * then model.custom_gcode_per_print_z should be updated considering this option
+ * */
+extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config);
 
 #ifndef NDEBUG
 // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index b26c42841..a8b88dd03 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -94,12 +94,13 @@ void BackgroundSlicingProcess::process_fff()
     m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
 #endif // ENABLE_THUMBNAIL_GENERATOR
 
+    /* #ys_FIXME_no_exported_codes
     if (m_fff_print->model().custom_gcode_per_print_z != GUI::wxGetApp().model().custom_gcode_per_print_z) {
         GUI::wxGetApp().model().custom_gcode_per_print_z = m_fff_print->model().custom_gcode_per_print_z;
-        // #ys_FIXME : controll text
         GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n"
                                     "Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info")));
     }
+    */
 
 	if (this->set_step_started(bspsGCodeFinalize)) {
 	    if (! m_export_path.empty()) {
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index 56752ab77..dbfd446b1 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -869,6 +869,9 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
         }
         // 4) Load the project config values (the per extruder wipe matrix etc).
         this->project_config.apply_only(config, s_project_options);
+
+        update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config);
+
         break;
     }
     case ptSLA:

From 9bf09e2874bdf4026867a9daa6a56378a546dccb Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 17 Dec 2019 16:50:00 +0100
Subject: [PATCH 13/17] Fix linking of OpenVDB in debug mode on multi conf
 generators.

---
 cmake/modules/FindOpenVDB.cmake | 50 +++++++++++++++++++++++++++------
 deps/CMakeLists.txt             |  1 +
 2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake
index 9afe8a235..8c91fac05 100644
--- a/cmake/modules/FindOpenVDB.cmake
+++ b/cmake/modules/FindOpenVDB.cmake
@@ -203,20 +203,44 @@ if(UNIX AND OPENVDB_USE_STATIC_LIBS)
 endif()
 
 set(OpenVDB_LIB_COMPONENTS "")
+set(OpenVDB_DEBUG_SUFFIX "d" CACHE STRING "Suffix for the debug libraries")
+
+get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 
 foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
   set(LIB_NAME ${COMPONENT})
-  find_library(OpenVDB_${COMPONENT}_LIBRARY ${LIB_NAME} lib${LIB_NAME}
+
+  find_library(OpenVDB_${COMPONENT}_LIBRARY_RELEASE ${LIB_NAME} lib${LIB_NAME}
     PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS}
     PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES}
   )
-  list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY})
 
-  if(OpenVDB_${COMPONENT}_LIBRARY)
-    set(OpenVDB_${COMPONENT}_FOUND TRUE)
-  else()
-    set(OpenVDB_${COMPONENT}_FOUND FALSE)
-  endif()
+  find_library(OpenVDB_${COMPONENT}_LIBRARY_DEBUG ${LIB_NAME}${OpenVDB_DEBUG_SUFFIX} lib${LIB_NAME}${OpenVDB_DEBUG_SUFFIX}
+    PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS}
+    PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES}
+  )
+
+  if (_is_multi)
+    list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE} ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG})
+
+    if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG})
+      set(OpenVDB_${COMPONENT}_FOUND TRUE)
+    else()
+      set(OpenVDB_${COMPONENT}_FOUND FALSE)
+    endif()
+  else ()
+    string(TOUPPER "${CMAKE_BUILD_TYPE}" _BUILD_TYPE)
+
+    set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}})
+
+    list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY})
+
+    if(OpenVDB_${COMPONENT}_LIBRARY)
+      set(OpenVDB_${COMPONENT}_FOUND TRUE)
+    else()
+      set(OpenVDB_${COMPONENT}_FOUND FALSE)
+    endif()
+  endif ()
 endforeach()
 
 if(UNIX AND OPENVDB_USE_STATIC_LIBS)
@@ -465,7 +489,6 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
   if(NOT TARGET OpenVDB::${COMPONENT})
     add_library(OpenVDB::${COMPONENT} UNKNOWN IMPORTED)
     set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
-      IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}"
       INTERFACE_COMPILE_OPTIONS "${OpenVDB_DEFINITIONS}"
       INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_INCLUDE_DIR}"
       IMPORTED_LINK_DEPENDENT_LIBRARIES "${_OPENVDB_HIDDEN_DEPENDENCIES}" # non visible deps
@@ -473,6 +496,17 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
       INTERFACE_COMPILE_FEATURES cxx_std_11
    )
 
+  if (_is_multi)
+    set_target_properties(OpenVDB::${COMPONENT} PROPERTIES 
+      IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}"
+      IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}"
+    )
+  else ()
+    set_target_properties(OpenVDB::${COMPONENT} PROPERTIES 
+      IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}"
+    )
+  endif ()
+
    if (OPENVDB_USE_STATIC_LIBS)
     set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
       INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB"
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index aeb078172..3935e38c3 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -47,6 +47,7 @@ message(STATUS "PrusaSlicer deps debug build: ${DEP_DEBUG}")
 
 find_package(Git REQUIRED)
 
+get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 
 function(prusaslicer_add_cmake_project projectname)
     cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})

From ab1bcad8c7f7217df49e93c4bf459594e69fb8a8 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 17 Dec 2019 16:56:11 +0100
Subject: [PATCH 14/17] Only consider openvdb debug if necessary.

---
 cmake/modules/FindOpenVDB.cmake | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake
index 8c91fac05..8d8c1089b 100644
--- a/cmake/modules/FindOpenVDB.cmake
+++ b/cmake/modules/FindOpenVDB.cmake
@@ -223,7 +223,9 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
   if (_is_multi)
     list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE} ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG})
 
-    if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG})
+    list(FIND CMAKE_CONFIGURATION_TYPES "Debug" _has_debug)
+    
+    if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND (_has_debug LESS 0 OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG))
       set(OpenVDB_${COMPONENT}_FOUND TRUE)
     else()
       set(OpenVDB_${COMPONENT}_FOUND FALSE)

From 2222b0cdc9ea4d1ae247aa0ed7fd8043c352166c Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 17 Dec 2019 17:08:43 +0100
Subject: [PATCH 15/17] Grab the release in debug mode if there is no debug
 when not on msvc

---
 cmake/modules/FindOpenVDB.cmake | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake
index 8d8c1089b..b75bc6ed0 100644
--- a/cmake/modules/FindOpenVDB.cmake
+++ b/cmake/modules/FindOpenVDB.cmake
@@ -235,6 +235,10 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
 
     set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}})
 
+    if (NOT MSVC AND NOT OpenVDB_${COMPONENT}_LIBRARY)
+      set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE})
+    endif ()
+
     list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY})
 
     if(OpenVDB_${COMPONENT}_LIBRARY)

From 7650732c61162d42e457d9db887961908cbd298d Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 18 Dec 2019 09:06:21 +0100
Subject: [PATCH 16/17] Show context menu for a multi-object selection on
 3DScene, add "Reload from disk" item for this menu

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 11 ++++++++---
 src/slic3r/GUI/GUI_ObjectList.hpp |  4 ++--
 src/slic3r/GUI/Plater.cpp         |  3 +++
 3 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index c0520cb9a..a058de805 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -3874,8 +3874,8 @@ void ObjectList::show_multi_selection_menu()
     GetSelections(sels);
 
     for (const wxDataViewItem& item : sels)
-        if (!(m_objects_model->GetItemType(item) & (itVolume | itObject)))
-            // show this menu only for Object(s)/Volume(s) selection
+        if (!(m_objects_model->GetItemType(item) & (itVolume | itObject | itInstance)))
+            // show this menu only for Objects(Instances mixed with Objects)/Volumes selection
             return;
 
     wxMenu* menu = new wxMenu();
@@ -3885,7 +3885,12 @@ void ObjectList::show_multi_selection_menu()
             _(L("Select extruder number for selected objects and/or parts")),
             [this](wxCommandEvent&) { extruder_selection(); }, "", menu);
 
-    PopupMenu(menu);
+    append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
+        [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() {
+        return wxGetApp().plater()->can_reload_from_disk();
+    }, wxGetApp().plater());
+
+    wxGetApp().plater()->PopupMenu(menu);
 }
 
 void ObjectList::extruder_selection()
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 73b23453b..a5a72ad8c 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -366,6 +366,8 @@ public:
     void update_printable_state(int obj_idx, int instance_idx);
     void toggle_printable_state(wxDataViewItem item);
 
+    void show_multi_selection_menu();
+
 private:
 #ifdef __WXOSX__
 //    void OnChar(wxKeyEvent& event);
@@ -384,8 +386,6 @@ private:
 	void OnEditingStarted(wxDataViewEvent &event);
 #endif /* __WXMSW__ */
     void OnEditingDone(wxDataViewEvent &event);
-
-    void show_multi_selection_menu();
     void extruder_selection();
     void set_extruder_for_selected_items(const int extruder) const ;
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index ef891b3b3..107d55de0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3615,7 +3615,10 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
         if (evt.data.second) // right button was clicked on empty space
             menu = &default_menu;
         else
+        {
+            sidebar->obj_list()->show_multi_selection_menu();
             return;
+        }
     }
     else
     {

From 8795f7dba86d0a7c08946be0d61513673c3d297e Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 18 Dec 2019 10:17:47 +0100
Subject: [PATCH 17/17] Trying to fix a compilation issue on Linux / OSX

---
 src/slic3r/GUI/wxExtensions.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 9b7de93cd..0c43be3a0 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -962,7 +962,7 @@ private:
         bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
         bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
 
-        int         tick;
+        int         tick = 0;
         std::string gcode = Slic3r::ColorChangeCode;
         int         extruder = 0;
         std::string color;