From cd3be74e3b3336bb46bbec85f6898813181caa79 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 23 Apr 2018 15:03:38 +0200
Subject: [PATCH 1/6] Workaround for 3D view and GUI buttons not in synch when
 object's size is almost identical to print volume's size

---
 xs/src/libslic3r/BoundingBox.hpp | 25 +++++++++++++++++++++++++
 xs/src/libslic3r/Model.cpp       |  4 ++--
 xs/src/libslic3r/Print.cpp       |  2 +-
 xs/src/slic3r/GUI/3DScene.cpp    |  2 +-
 4 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index 92a2bd451..a96fd1a30 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -147,6 +147,31 @@ public:
     BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
     BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
     BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
+
+    // check if the given point is contained inside this bounding box
+    // all quantities are compared after being quantized to try to reduce instability due to float imprecision
+    bool contains_quantized(const Pointf3& point) const {
+        struct Helper
+        {
+            static coordf_t quantize(coordf_t value)
+            {
+                static const coordf_t INV_EPSILON = 1.0 / EPSILON;
+                return round(value * INV_EPSILON + 0.5) * EPSILON;
+            }
+        };
+
+        coordf_t x = Helper::quantize(point.x);
+        coordf_t y = Helper::quantize(point.y);
+        coordf_t z = Helper::quantize(point.z);
+
+        return (Helper::quantize(min.x) <= x) && (x <= Helper::quantize(max.x))
+            && (Helper::quantize(min.y) <= y) && (y <= Helper::quantize(max.y))
+            && (Helper::quantize(min.z) <= z) && (z <= Helper::quantize(max.z));
+    }
+
+    bool contains_quantized(const BoundingBoxf3& other) const {
+        return contains_quantized(other.min) && contains_quantized(other.max);
+    }
 };
 
 template<typename VT>
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index aaea863e8..25e207304 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -463,7 +463,7 @@ bool Model::fits_print_volume(const DynamicPrintConfig* config) const
     BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
     // Allow the objects to protrude below the print bed
     print_volume.min.z = -1e10;
-    return print_volume.contains(transformed_bounding_box());
+    return print_volume.contains_quantized(transformed_bounding_box());
 }
 
 bool Model::fits_print_volume(const FullPrintConfig &config) const
@@ -474,7 +474,7 @@ bool Model::fits_print_volume(const FullPrintConfig &config) const
     BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
     // Allow the objects to protrude below the print bed
     print_volume.min.z = -1e10;
-    return print_volume.contains(transformed_bounding_box());
+    return print_volume.contains_quantized(transformed_bounding_box());
 }
 
 unsigned int Model::get_auto_extruder_id()
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index e692b1e9e..9de5c0379 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -522,7 +522,7 @@ std::string Print::validate() const
     // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
     print_volume.min.z = -1e10;
     for (PrintObject *po : this->objects) {
-        if (! print_volume.contains(po->model_object()->tight_bounding_box(false)))
+        if (!print_volume.contains_quantized(po->model_object()->tight_bounding_box(false)))
             return "Some objects are outside of the print volume.";
     }
 
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 7f5e632bb..7d4e4d68c 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -665,7 +665,7 @@ void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config,
             continue;
         }
 
-        volume->is_outside = !print_volume.contains(volume->transformed_bounding_box());
+        volume->is_outside = !print_volume.contains_quantized(volume->transformed_bounding_box());
     }
 }
 

From 71d9500b2e7fe37ee4de8ae6d52484285ddfc7a8 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 24 Apr 2018 09:00:33 +0200
Subject: [PATCH 2/6] More robust fix for 3D view and GUI buttons not in synch
 when object's size is almost identical to print volume's size

---
 lib/Slic3r/GUI/3DScene.pm        |  2 +-
 lib/Slic3r/GUI/Plater.pm         |  2 +-
 lib/Slic3r/GUI/Plater/3D.pm      |  5 +++--
 xs/src/libslic3r/BoundingBox.hpp | 25 -------------------------
 xs/src/libslic3r/Model.cpp       | 30 ------------------------------
 xs/src/libslic3r/Model.hpp       |  4 ----
 xs/src/libslic3r/Print.cpp       |  2 +-
 xs/src/slic3r/GUI/3DScene.cpp    | 25 ++++++++++++++++++-------
 xs/src/slic3r/GUI/3DScene.hpp    |  4 +++-
 xs/xsp/GUI_3DScene.xsp           |  7 ++++++-
 xs/xsp/Model.xsp                 |  3 ---
 11 files changed, 33 insertions(+), 76 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index ff6d73399..80fa337c4 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -1396,7 +1396,7 @@ sub Render {
         if ($self->enable_picking) {
             $self->mark_volumes_for_layer_height;
             $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height'));
-            $self->volumes->update_outside_state($self->{config}, 0);
+            $self->volumes->check_outside_state($self->{config});
             # do not cull backfaces to show broken geometry, if any
             glDisable(GL_CULL_FACE);
         }
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index ce78eab8e..f9ce75eff 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1921,7 +1921,7 @@ sub object_list_changed {
     }
 
     my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file};
-    my $model_fits = $self->{model}->fits_print_volume($self->{config});
+    my $model_fits = $self->{canvas3D} ? $self->{canvas3D}->volumes->check_outside_state($self->{config}) : 1;
     my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable';
     $self->{"btn_$_"}->$method
         for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index dce60e2c4..54b916773 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -222,13 +222,14 @@ sub reload_scene {
     # checks for geometry outside the print volume to render it accordingly
     if (scalar @{$self->volumes} > 0)
     {
-        if (!$self->{model}->fits_print_volume($self->{config})) {
+        my $contained = $self->volumes->check_outside_state($self->{config});
+        if (!$contained) {
             $self->set_warning_enabled(1);
             Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume"));
             $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons);
         } else {
             $self->set_warning_enabled(0);
-            $self->volumes->update_outside_state($self->{config}, 1);
+            $self->volumes->reset_outside_state();
             Slic3r::GUI::_3DScene::reset_warning_texture();
             $self->on_enable_action_buttons->(1) if ($self->on_enable_action_buttons);
         }
diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index a96fd1a30..92a2bd451 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -147,31 +147,6 @@ public:
     BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
     BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
     BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
-
-    // check if the given point is contained inside this bounding box
-    // all quantities are compared after being quantized to try to reduce instability due to float imprecision
-    bool contains_quantized(const Pointf3& point) const {
-        struct Helper
-        {
-            static coordf_t quantize(coordf_t value)
-            {
-                static const coordf_t INV_EPSILON = 1.0 / EPSILON;
-                return round(value * INV_EPSILON + 0.5) * EPSILON;
-            }
-        };
-
-        coordf_t x = Helper::quantize(point.x);
-        coordf_t y = Helper::quantize(point.y);
-        coordf_t z = Helper::quantize(point.z);
-
-        return (Helper::quantize(min.x) <= x) && (x <= Helper::quantize(max.x))
-            && (Helper::quantize(min.y) <= y) && (y <= Helper::quantize(max.y))
-            && (Helper::quantize(min.z) <= z) && (z <= Helper::quantize(max.z));
-    }
-
-    bool contains_quantized(const BoundingBoxf3& other) const {
-        return contains_quantized(other.min) && contains_quantized(other.max);
-    }
 };
 
 template<typename VT>
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 25e207304..9c9b56659 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -447,36 +447,6 @@ void Model::adjust_min_z()
     }
 }
 
-bool Model::fits_print_volume(const DynamicPrintConfig* config) const
-{
-    if (config == nullptr)
-        return false;
-
-    if (objects.empty())
-        return true;
-
-    const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
-    if (opt == nullptr)
-        return false;
-
-    BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
-    BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
-    // Allow the objects to protrude below the print bed
-    print_volume.min.z = -1e10;
-    return print_volume.contains_quantized(transformed_bounding_box());
-}
-
-bool Model::fits_print_volume(const FullPrintConfig &config) const
-{
-    if (objects.empty())
-        return true;
-    BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values));
-    BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
-    // Allow the objects to protrude below the print bed
-    print_volume.min.z = -1e10;
-    return print_volume.contains_quantized(transformed_bounding_box());
-}
-
 unsigned int Model::get_auto_extruder_id()
 {
     unsigned int id = s_auto_extruder_id;
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index 0c0ffe776..0baea979c 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -285,10 +285,6 @@ public:
     // Ensures that the min z of the model is not negative
     void adjust_min_z();
 
-    // Returs true if this model is contained into the print volume defined inside the given config
-    bool fits_print_volume(const DynamicPrintConfig* config) const;
-    bool fits_print_volume(const FullPrintConfig &config) const;
-
     void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
 
     static unsigned int get_auto_extruder_id();
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 9de5c0379..585977ba7 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -522,7 +522,7 @@ std::string Print::validate() const
     // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
     print_volume.min.z = -1e10;
     for (PrintObject *po : this->objects) {
-        if (!print_volume.contains_quantized(po->model_object()->tight_bounding_box(false)))
+        if (!print_volume.contains(po->model_object()->tight_bounding_box(false)))
             return "Some objects are outside of the print volume.";
     }
 
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 7d4e4d68c..b9ac1966e 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -643,29 +643,40 @@ void GLVolumeCollection::render_legacy() const
     glDisable(GL_BLEND);
 }
 
-void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, bool all_inside)
+bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
 {
     if (config == nullptr)
-        return;
+        return false;
 
     const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
     if (opt == nullptr)
-        return;
+        return false;
 
     BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
     BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
     // Allow the objects to protrude below the print bed
     print_volume.min.z = -1e10;
 
+    bool contained = true;
     for (GLVolume* volume : this->volumes)
     {
-        if (all_inside)
+        if (volume != nullptr)
         {
-            volume->is_outside = false;
-            continue;
+            bool state = print_volume.contains(volume->transformed_bounding_box());
+            contained &= state;
+            volume->is_outside = !state;
         }
+    }
 
-        volume->is_outside = !print_volume.contains_quantized(volume->transformed_bounding_box());
+    return contained;
+}
+
+void GLVolumeCollection::reset_outside_state()
+{
+    for (GLVolume* volume : this->volumes)
+    {
+        if (volume != nullptr)
+            volume->is_outside = false;
     }
 }
 
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 8f03e4774..fb5d9f14b 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -422,7 +422,9 @@ public:
         print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
     }
 
-    void update_outside_state(const DynamicPrintConfig* config, bool all_inside);
+    bool check_outside_state(const DynamicPrintConfig* config);
+    void reset_outside_state();
+
     void update_colors_by_extruder(const DynamicPrintConfig* config);
 
     // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 86d0aeba2..ee5a82ba1 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -104,7 +104,12 @@
     void release_geometry();
 
     void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z);
-    void update_outside_state(DynamicPrintConfig* config, bool all_inside);
+    bool check_outside_state(DynamicPrintConfig* config)
+        %code%{ 
+                RETVAL = THIS->check_outside_state(config);
+        %};
+    
+    void reset_outside_state();    
     void update_colors_by_extruder(DynamicPrintConfig* config);
 
     bool move_volume_up(int idx)
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 702839537..02017839a 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -99,9 +99,6 @@
 
     void print_info() const;
 
-    bool fits_print_volume(DynamicPrintConfig* config) const
-        %code%{ RETVAL = THIS->fits_print_volume(config); %};
-
     bool store_stl(char *path, bool binary)
         %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %};
     bool store_amf(char *path, Print* print, bool export_print_config)

From 86155ae4c00e2146ca19f42e6eca240d3d8b64fa Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Wed, 9 May 2018 10:28:26 +0200
Subject: [PATCH 3/6] Fixed conflict in previous merge

---
 xs/src/libslic3r/Model.cpp | 30 ------------------------------
 1 file changed, 30 deletions(-)

diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 755941144..8ce23b1e5 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -451,36 +451,6 @@ void Model::adjust_min_z()
     }
 }
 
-bool Model::fits_print_volume(const DynamicPrintConfig* config) const
-{
-    if (config == nullptr)
-        return false;
-
-    if (objects.empty())
-        return true;
-
-    const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
-    if (opt == nullptr)
-        return false;
-
-    BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
-    BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
-    // Allow the objects to protrude below the print bed
-    print_volume.min.z = -1e10;
-    return print_volume.contains(transformed_bounding_box());
-}
-
-bool Model::fits_print_volume(const FullPrintConfig &config) const
-{
-    if (objects.empty())
-        return true;
-    BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values));
-    BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
-    // Allow the objects to protrude below the print bed
-    print_volume.min.z = -1e10;
-    return print_volume.contains(transformed_bounding_box());
-}
-
 unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
 {
     unsigned int id = s_auto_extruder_id;

From de0b8226e1eaf26a529b65af87598daf50ca3350 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 17 May 2018 16:02:31 +0200
Subject: [PATCH 4/6] Tweaks to logic for sliders update in 3D Preview

---
 lib/Slic3r/GUI/Plater/3DPreview.pm | 64 +++++++++++++++++++++---------
 1 file changed, 45 insertions(+), 19 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm
index 6b448080d..fec463682 100644
--- a/lib/Slic3r/GUI/Plater/3DPreview.pm
+++ b/lib/Slic3r/GUI/Plater/3DPreview.pm
@@ -328,22 +328,12 @@ sub load_print {
     }
 
     if ($n_layers == 0) {
-        $self->enabled(0);
-        $self->set_z_range(0,0);
-        $self->slider_low->Hide;
-        $self->slider_high->Hide;
-        $self->{z_label_low}->SetLabel("");
-        $self->{z_label_high}->SetLabel("");
-        $self->{z_label_low_idx}->SetLabel("");
-        $self->{z_label_high_idx}->SetLabel("");
+        $self->reset_sliders;
         $self->canvas->reset_legend_texture();
         $self->canvas->Refresh;  # clears canvas
         return;
     }
     
-    # used to set the sliders to the extremes of the current zs range
-    $self->{force_sliders_full_range} = 0;
-    
     if ($self->{preferred_color_mode} eq 'tool_or_feature') {
         # It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature.
         # Color by feature if it is a single extruder print.
@@ -369,6 +359,9 @@ sub load_print {
     }
 
     if ($self->IsShown) {
+        # used to set the sliders to the extremes of the current zs range
+        $self->{force_sliders_full_range} = 0;
+
         if ($self->gcode_preview_data->empty) {
             # load skirt and brim
             $self->canvas->load_print_toolpaths($self->print, \@colors);
@@ -389,13 +382,30 @@ sub load_print {
             # recalculates zs and update sliders accordingly
             $self->{layers_z} = $self->canvas->get_current_print_zs();
             $n_layers = scalar(@{$self->{layers_z}});            
-        }
+            if ($n_layers == 0) {
+                # all layers filtered out
+                $self->reset_sliders;
+                $self->canvas->Refresh;  # clears canvas
+            }
+       }
 
-        $self->update_sliders($n_layers);
+        $self->update_sliders($n_layers) if ($n_layers > 0);
         $self->_loaded(1);
     }
 }
 
+sub reset_sliders {
+    my ($self) = @_;
+    $self->enabled(0);
+    $self->set_z_range(0,0);
+    $self->slider_low->Hide;
+    $self->slider_high->Hide;
+    $self->{z_label_low}->SetLabel("");
+    $self->{z_label_high}->SetLabel("");
+    $self->{z_label_low_idx}->SetLabel("");
+    $self->{z_label_high_idx}->SetLabel("");
+}
+
 sub update_sliders
 {
     my ($self, $n_layers) = @_;
@@ -410,18 +420,32 @@ sub update_sliders
         $z_idx_low = 0;
         $z_idx_high = $n_layers - 1;
     } elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
-        # use $z_idx
-    } else {
+        # search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown)
+        if (defined($self->{z_low})) {
+            for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
+                if ($self->{layers_z}[$i] <= $self->{z_low}) {
+                    $z_idx_low = $i;
+                    last;
+                }
+            }
+        }
+        if (defined($self->{z_high})) {
+            for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
+                if ($self->{layers_z}[$i] <= $self->{z_high}) {
+                    $z_idx_high = $i;
+                    last;
+                }
+            }
+        }
+    } elsif ($z_idx_high >= $n_layers) {
         # Out of range. Disable 'single layer' view.
         $self->single_layer(0);
         $self->{checkbox_singlelayer}->SetValue(0);
         $z_idx_low = 0;
         $z_idx_high = $n_layers - 1;
-    }
-    if ($self->single_layer) {
-        $z_idx_low = $z_idx_high;
-    } elsif ($z_idx_low > $z_idx_high) {
+    } else {
         $z_idx_low = 0;
+        $z_idx_high = $n_layers - 1;
     }
     
     $self->slider_low->SetValue($z_idx_low);
@@ -437,6 +461,8 @@ sub set_z_range
     my ($self, $z_low, $z_high) = @_;
     
     return if !$self->enabled;
+    $self->{z_low} = $z_low;
+    $self->{z_high} = $z_high;
     $self->{z_label_low}->SetLabel(sprintf '%.2f', $z_low);
     $self->{z_label_high}->SetLabel(sprintf '%.2f', $z_high);
     my $z_idx_low = 1 + $self->slider_low->GetValue;

From 0584990b65ccc9304c3511f30f3ceb44331f2f91 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 18 May 2018 10:14:47 +0200
Subject: [PATCH 5/6] Fixed z layers indices under 3D preview sliders

---
 lib/Slic3r/GUI/3DScene.pm          |  6 ++----
 lib/Slic3r/GUI/Plater/3DPreview.pm | 21 ++++++++++++++++-----
 xs/src/slic3r/GUI/3DScene.cpp      |  4 ++--
 xs/src/slic3r/GUI/3DScene.hpp      |  2 +-
 xs/xsp/GUI_3DScene.xsp             |  5 ++---
 5 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 3bfb6f759..33c0e8d37 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -2192,10 +2192,8 @@ sub reset_legend_texture {
 }
 
 sub get_current_print_zs {
-    my ($self) = @_;
-    
-    my $count = $self->volumes->get_current_print_zs();
-    return $count;
+    my ($self, $active_only) = @_;
+    return $self->volumes->get_current_print_zs($active_only);
 }
 
 1;
diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm
index fec463682..de6c6e818 100644
--- a/lib/Slic3r/GUI/Plater/3DPreview.pm
+++ b/lib/Slic3r/GUI/Plater/3DPreview.pm
@@ -380,7 +380,7 @@ sub load_print {
             $self->show_hide_ui_elements('full');
 
             # recalculates zs and update sliders accordingly
-            $self->{layers_z} = $self->canvas->get_current_print_zs();
+            $self->{layers_z} = $self->canvas->get_current_print_zs(1);
             $n_layers = scalar(@{$self->{layers_z}});            
             if ($n_layers == 0) {
                 # all layers filtered out
@@ -465,10 +465,21 @@ sub set_z_range
     $self->{z_high} = $z_high;
     $self->{z_label_low}->SetLabel(sprintf '%.2f', $z_low);
     $self->{z_label_high}->SetLabel(sprintf '%.2f', $z_high);
-    my $z_idx_low = 1 + $self->slider_low->GetValue;
-    my $z_idx_high = 1 + $self->slider_high->GetValue;
-    $self->{z_label_low_idx}->SetLabel(sprintf '%d', $z_idx_low);
-    $self->{z_label_high_idx}->SetLabel(sprintf '%d', $z_idx_high);
+    
+    my $layers_z = $self->canvas->get_current_print_zs(0);
+    for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) {
+        if (($z_low - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_low + 1e-6)) {
+            $self->{z_label_low_idx}->SetLabel(sprintf '%d', $i + 1);
+            last;
+        }
+    }
+    for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) {
+        if (($z_high - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_high + 1e-6)) {
+            $self->{z_label_high_idx}->SetLabel(sprintf '%d', $i + 1);
+            last;
+        }
+    }
+
     $self->canvas->set_toolpaths_range($z_low - 1e-6, $z_high + 1e-6);
     $self->canvas->Refresh if $self->IsShown;
 }
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 0ead76b88..db1737e14 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -760,13 +760,13 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
     }
 }
 
-std::vector<double> GLVolumeCollection::get_current_print_zs() const
+std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const
 {
     // Collect layer top positions of all volumes.
     std::vector<double> print_zs;
     for (GLVolume *vol : this->volumes)
     {
-        if (vol->is_active)
+        if (!active_only || vol->is_active)
             append(print_zs, vol->print_zs);
     }
     std::sort(print_zs.begin(), print_zs.end());
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index fb5d9f14b..a417f5f9d 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -428,7 +428,7 @@ public:
     void update_colors_by_extruder(const DynamicPrintConfig* config);
 
     // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
-    std::vector<double> get_current_print_zs() const;
+    std::vector<double> get_current_print_zs(bool active_only) const;
 
 private:
     GLVolumeCollection(const GLVolumeCollection &other);
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index ee5a82ba1..6a907291e 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -92,10 +92,9 @@
     int count()
         %code{% RETVAL = THIS->volumes.size(); %};
         
-    std::vector<double> get_current_print_zs()
-        %code{% RETVAL = THIS->get_current_print_zs(); %};
+    std::vector<double> get_current_print_zs(bool active_only)
+        %code{% RETVAL = THIS->get_current_print_zs(active_only); %};
 
-        
     void set_range(double low, double high);
 
     void render_VBOs() const;

From 3291cbfdad7f1172016147befb3ec4c2b89d5ffb Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 21 May 2018 11:19:03 +0200
Subject: [PATCH 6/6] Fix of #889

---
 xs/src/slic3r/GUI/3DScene.cpp | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index db1737e14..cb2e08e1f 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -2457,7 +2457,7 @@ bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVol
         TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type));
         if (type != types.end())
         {
-            type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z));
+            type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
             type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size());
             type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size());
 
@@ -2523,7 +2523,7 @@ bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, G
         FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate));
         if (feedrate != feedrates.end())
         {
-            feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z));
+            feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
             feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size());
             feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size());
 
@@ -2589,7 +2589,7 @@ bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVol
         ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
         if (tool != tools.end())
         {
-            tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z));
+            tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
             tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size());
             tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size());
 
@@ -2613,7 +2613,10 @@ void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLV
     {
         volumes.volumes.emplace_back(volume);
 
-        for (const GCodePreviewData::Retraction::Position& position : preview_data.retraction.positions)
+        GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions);
+        std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
+
+        for (const GCodePreviewData::Retraction::Position& position : copy)
         {
             volume->print_zs.push_back(unscale(position.position.z));
             volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
@@ -2641,7 +2644,10 @@ void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, G
     {
         volumes.volumes.emplace_back(volume);
 
-        for (const GCodePreviewData::Retraction::Position& position : preview_data.unretraction.positions)
+        GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions);
+        std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
+
+        for (const GCodePreviewData::Retraction::Position& position : copy)
         {
             volume->print_zs.push_back(unscale(position.position.z));
             volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());