From 20cff0f630d37181beabec8baa7010f2b90c8ed1 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 09:00:17 +0100
Subject: [PATCH 01/15] Fixed crash when selecting wipe tower

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

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index f18b1e71c..3fc090c6b 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1383,7 +1383,7 @@ bool GLCanvas3D::Selection::is_single_full_instance() const
         return false;
 
     int object_idx = m_valid ? get_object_idx() : -1;
-    if (object_idx == -1)
+    if ((object_idx < 0) || ((int)m_model->objects.size() < object_idx))
         return false;
 
     int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx();

From 512677ed7365bda84613da144853b15f23055305 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 09:01:58 +0100
Subject: [PATCH 02/15] Fixed typo

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

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 3fc090c6b..ceb59bea1 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1383,7 +1383,7 @@ bool GLCanvas3D::Selection::is_single_full_instance() const
         return false;
 
     int object_idx = m_valid ? get_object_idx() : -1;
-    if ((object_idx < 0) || ((int)m_model->objects.size() < object_idx))
+    if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx))
         return false;
 
     int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx();

From b4216fbda99c35439180f7c6bd4690e89a84f81d Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 7 Dec 2018 09:32:54 +0100
Subject: [PATCH 03/15] Clamp the invalid extruders to the default extruder
 (with index 1), not to the last extruder. This is consistent with the UI
 preview.

---
 src/libslic3r/Print.cpp | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index ad7620e19..0a56217d3 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -360,13 +360,20 @@ double Print::max_allowed_layer_height() const
     return nozzle_diameter_max;
 }
 
+static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
+{
+    if (opt.value > (int)num_extruders)
+        // assign the default extruder
+        opt.value = 1;
+}
+
 static PrintObjectConfig object_config_from_model(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders)
 {
     PrintObjectConfig config = default_object_config;
     normalize_and_apply_config(config, object.config);
-    // Clamp extruders to the number of extruders this printer is physically equipped with.
-    config.support_material_extruder.value           = std::min(config.support_material_extruder.value,           (int)num_extruders);
-    config.support_material_interface_extruder.value = std::min(config.support_material_interface_extruder.value, (int)num_extruders);
+    // Clamp invalid extruders to the default extruder (with index 1).
+    clamp_exturder_to_default(config.support_material_extruder,           num_extruders);
+    clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders);
     return config;
 }
 
@@ -377,10 +384,10 @@ static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig
     normalize_and_apply_config(config, volume.config);
     if (! volume.material_id().empty())
         normalize_and_apply_config(config, volume.material()->config);
-    // Clamp extruders to the number of extruders this printer is physically equipped with.
-    config.infill_extruder.value        = std::min(config.infill_extruder.value,        (int)num_extruders);
-    config.perimeter_extruder.value     = std::min(config.perimeter_extruder.value,     (int)num_extruders);
-    config.solid_infill_extruder.value  = std::min(config.solid_infill_extruder.value,  (int)num_extruders);
+    // Clamp invalid extruders to the default extruder (with index 1).
+    clamp_exturder_to_default(config.infill_extruder,       num_extruders);
+    clamp_exturder_to_default(config.perimeter_extruder,    num_extruders);
+    clamp_exturder_to_default(config.solid_infill_extruder, num_extruders);
     return config;
 }
 

From ec2d9d6d085c1d1f872d635d0404dde831ab78b1 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 10:01:30 +0100
Subject: [PATCH 04/15] Changed starting orientation of rotate gizmo grabbers

---
 src/slic3r/GUI/GLGizmo.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp
index 2f39f7d69..f24a4f098 100644
--- a/src/slic3r/GUI/GLGizmo.cpp
+++ b/src/slic3r/GUI/GLGizmo.cpp
@@ -642,13 +642,13 @@ void GLGizmoRotate::transform_to_local() const
     case X:
     {
         ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
-        ::glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
+        ::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
         break;
     }
     case Y:
     {
-        ::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
-        ::glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
+        ::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
+        ::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
         break;
     }
     default:
@@ -670,14 +670,14 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray) cons
     {
     case X:
     {
-        m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitZ()));
+        m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ()));
         m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY()));
         break;
     }
     case Y:
     {
-        m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitZ()));
-        m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitX()));
+        m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitY()));
+        m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ()));
         break;
     }
     default:

From b1f819f83b629655562e332490c8f7a50fc63c6b Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 10:36:43 +0100
Subject: [PATCH 05/15] Fixed rendering of sla support points when object's
 instance transformation contains mirror

---
 src/slic3r/GUI/GLGizmo.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp
index f24a4f098..239b45702 100644
--- a/src/slic3r/GUI/GLGizmo.cpp
+++ b/src/slic3r/GUI/GLGizmo.cpp
@@ -1732,7 +1732,9 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent)
 #if ENABLE_SLA_SUPPORT_GIZMO_MOD
     m_quadric = ::gluNewQuadric();
     if (m_quadric != nullptr)
-        ::gluQuadricDrawStyle(m_quadric, GLU_FILL);
+        // using GLU_FILL does not work when the instance's transformation
+        // contains mirroring (normals are reverted)
+        ::gluQuadricDrawStyle(m_quadric, GLU_SILHOUETTE);
 #endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 }
 
@@ -1881,7 +1883,8 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection,
         ::glColor3fv(render_color);
         ::glPushMatrix();
         ::glTranslated(m_grabbers[i].center(0), m_grabbers[i].center(1), m_grabbers[i].center(2));
-        ::gluSphere(m_quadric, 0.75, 36, 18);
+        ::gluQuadricDrawStyle(m_quadric, GLU_SILHOUETTE);
+        ::gluSphere(m_quadric, 0.75, 64, 32);
         ::glPopMatrix();
     }
 

From 9f43e7d375f06fe4483ca80c07800da39160ffaa Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 7 Dec 2018 11:21:05 +0100
Subject: [PATCH 06/15] Fixed a harless assert in PrintObject consturctor.
 Fixed a bug in G-code generator regarding the seam hiding.

---
 src/libslic3r/GCode.cpp       | 4 +++-
 src/libslic3r/Print.cpp       | 6 +++---
 src/libslic3r/Print.hpp       | 2 +-
 src/libslic3r/PrintObject.cpp | 4 ++--
 4 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 9a6635f40..7f56b5f99 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -2159,9 +2159,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
         m_wipe.path = paths.front().polyline;  // TODO: don't limit wipe to last path
     
     // make a little move inwards before leaving loop
-    if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1) {
+	if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) {
         // detect angle between last and first segment
         // the side depends on the original winding order of the polygon (left for contours, right for holes)
+		//FIXME improve the algorithm in case the loop is tiny.
+		//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
         Point a = paths.front().polyline.points[1];  // second point
         Point b = *(paths.back().polyline.points.end()-3);       // second to last point
         if (was_clockwise) {
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 0a56217d3..fb6d90d60 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -400,7 +400,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
     m_model.objects.emplace_back(ModelObject::new_copy(*model_object));
     m_model.objects.back()->set_model(&m_model);
     // Initialize a new print object and store it at the given position.
-    PrintObject *object = new PrintObject(this, model_object);
+    PrintObject *object = new PrintObject(this, model_object, true);
     if (idx != -1) {
         delete m_objects[idx];
         m_objects[idx] = object;
@@ -964,7 +964,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
             if (old.empty()) {
                 // Simple case, just generate new instances.
                 for (const PrintInstances &print_instances : new_print_instances) {
-                    PrintObject *print_object = new PrintObject(this, model_object);
+                    PrintObject *print_object = new PrintObject(this, model_object, false);
 					print_object->set_trafo(print_instances.trafo);
                     print_object->set_copies(print_instances.copies);
                     print_object->config_apply(config);
@@ -983,7 +983,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
 				for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
 				if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
                     // This is a new instance (or a set of instances with the same trafo). Just add it.
-                    PrintObject *print_object = new PrintObject(this, model_object);
+                    PrintObject *print_object = new PrintObject(this, model_object, false);
                     print_object->set_trafo(new_instances.trafo);
                     print_object->set_copies(new_instances.copies);
                     print_object->config_apply(config);
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 4175d4f22..872ce31db 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -159,7 +159,7 @@ protected:
     // to be called from Print only.
     friend class Print;
 
-	PrintObject(Print* print, ModelObject* model_object);
+	PrintObject(Print* print, ModelObject* model_object, bool add_instances = true);
 	~PrintObject() {}
 
     void                    config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 371e62ff9..10f5a1ac2 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -34,7 +34,7 @@
 
 namespace Slic3r {
 
-PrintObject::PrintObject(Print* print, ModelObject* model_object) :
+PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
     PrintObjectBaseWithState(print, model_object),
     typed_slices(false),
     size(Vec3crd::Zero()),
@@ -54,7 +54,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object) :
         this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
     }
     
-    {
+    if (add_instances) {
         Points copies;
         copies.reserve(m_model_object->instances.size());
         for (const ModelInstance *mi : m_model_object->instances) {

From 8fc723b746b4f7067bf705f5515627b3b887ec8f Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 11:24:29 +0100
Subject: [PATCH 07/15] Fixed wipe tower translation

---
 src/slic3r/GUI/GLCanvas3D.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index ceb59bea1..1cb8622de 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1458,13 +1458,13 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement)
     for (unsigned int i : m_list)
     {
 #if ENABLE_MODELVOLUME_TRANSFORM
-        if (m_mode == Instance)
-            (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
-        else if (m_mode == Volume)
-        {
-            Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix()).inverse() * displacement;
-            (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
-        }
+    if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower)
+    {
+        Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix()).inverse() * displacement;
+        (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
+    }
+    else if (m_mode == Instance)
+        (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
 #else
         (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].get_position() + displacement);
 #endif // ENABLE_MODELVOLUME_TRANSFORM

From 0c7c9d5754ca809bca7a7f87e3f296d1f11a4a1c Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Wed, 5 Dec 2018 15:22:03 +0100
Subject: [PATCH 08/15] Plater: single object STL export and reloading Based on
 Plater.pm`export_object_stl() and reload_from_disk()

---
 lib/Slic3r/GUI/Plater.pm  |  4 +--
 src/slic3r/GUI/Plater.cpp | 56 ++++++++++++++++++++++++++++++++-------
 src/slic3r/GUI/Plater.hpp |  2 +-
 3 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index d2a7a23b1..db360c811 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1876,7 +1876,7 @@ sub export_stl {
     $self->statusbar->SetStatusText(L("STL file exported to ").$output_file);
 }
 
-# XXX: not done
+# XXX: VK: done
 sub reload_from_disk {
     my ($self) = @_;
     
@@ -1908,7 +1908,7 @@ sub reload_from_disk {
     $self->remove($obj_idx);
 }
 
-# XXX: VK: done
+# XXX: VK: integrated into Plater::export_stl()
 sub export_object_stl {
     my ($self) = @_;
     my ($obj_idx, $object) = $self->selected_object;
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 6d2af3b8d..2a99d2c5c 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -911,7 +911,7 @@ struct Plater::priv
     wxMenuItem* item_sla_autorot = nullptr;
 
     // Data
-    Slic3r::DynamicPrintConfig *config;
+    Slic3r::DynamicPrintConfig *config;        // FIXME: leak?
     Slic3r::Print               fff_print;
 	Slic3r::SLAPrint            sla_print;
     Slic3r::Model               model;
@@ -1007,7 +1007,6 @@ struct Plater::priv
     unsigned int update_background_process();
     void async_apply_config();
     void reload_from_disk();
-    void export_object_stl();
     void fix_through_netfabb(const int obj_idx);
 
 #if ENABLE_REMOVE_TABS_FROM_PLATER
@@ -2088,12 +2087,33 @@ void Plater::priv::update_sla_scene()
 
 void Plater::priv::reload_from_disk()
 {
-    // TODO
-}
+    const auto &selection = get_selection();
+    const auto obj_orig_idx = selection.get_object_idx();
+    if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; }
 
-void Plater::priv::export_object_stl()
-{
-    // TODO
+    auto *object_orig = model.objects[obj_orig_idx];
+    std::vector<fs::path> input_paths(1, object_orig->input_file);
+
+    const auto new_idxs = load_files(input_paths, true, false);
+
+    for (const auto idx : new_idxs) {
+        ModelObject *object = model.objects[idx];
+
+        object->clear_instances();
+        for (const ModelInstance *instance : object_orig->instances) {
+            object->add_instance(*instance);
+        }
+
+        if (object->volumes.size() == object_orig->volumes.size()) {
+            for (size_t i = 0; i < object->volumes.size(); i++) {
+                object->volumes[i]->config.apply(object_orig->volumes[i]->config);
+            }
+        }
+
+        // XXX: Restore more: layer_height_ranges, layer_height_profile, layer_height_profile_valid (?)
+    }
+
+    remove(obj_orig_idx);
 }
 
 void Plater::priv::fix_through_netfabb(const int obj_idx)
@@ -2477,6 +2497,12 @@ bool Plater::priv::init_object_menu()
 
     wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png");
 
+    append_menu_item(&object_menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
+        [this](wxCommandEvent&) { reload_from_disk(); });
+
+    append_menu_item(&object_menu, wxID_ANY, _(L("Export object as STL…")), _(L("Export this single object as STL file")),
+        [this](wxCommandEvent&) { q->export_stl(true); });
+
     // Add the automatic rotation sub-menu
     item_sla_autorot = append_menu_item(&object_menu, wxID_ANY, _(L("Optimize orientation")), _(L("Optimize the rotation of the object for better print results.")),
                                             [this](wxCommandEvent&) { sla_optimize_rotation(); });
@@ -2845,7 +2871,7 @@ void Plater::export_gcode(fs::path output_path)
     }
 }
 
-void Plater::export_stl()
+void Plater::export_stl(bool selection_only)
 {
     if (p->model.objects.empty()) { return; }
 
@@ -2855,7 +2881,19 @@ void Plater::export_stl()
     // Store a binary STL
     wxString path = dialog->GetPath();
     auto path_cstr = path.c_str();
-    auto mesh = p->model.mesh();
+
+    TriangleMesh mesh;
+    if (selection_only) {
+        const auto &selection = p->get_selection();
+        if (selection.is_wipe_tower()) { return; }
+
+        const auto obj_idx = selection.get_object_idx();
+        if (obj_idx == -1) { return; }
+        mesh = p->model.objects[obj_idx]->mesh();
+    } else {
+        auto mesh = p->model.mesh();
+    }
+
     Slic3r::store_stl(path_cstr, &mesh, true);
     p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path));
 }
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 74dd790d2..242a7b39f 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -146,7 +146,7 @@ public:
 
     // Note: empty path means "use the default"
     void export_gcode(boost::filesystem::path output_path = boost::filesystem::path());
-    void export_stl();
+    void export_stl(bool selection_only = false);
     void export_amf();
     void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
     void reslice();

From c23c09c453aff7b9bb25d7977f1ba5c41f329fd3 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Fri, 7 Dec 2018 12:36:30 +0100
Subject: [PATCH 09/15] Cut: Fix modifier transformation

---
 src/libslic3r/Model.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 250c1c968..84c34e232 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -987,6 +987,8 @@ bool ModelObject::needed_repair() const
 
 ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
 {
+    if (!keep_upper && !keep_lower) { return {}; }
+
     // Clone the object to duplicate instances, materials etc.
     ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr;
     ModelObject* lower = keep_lower ? ModelObject::new_clone(*this) : nullptr;
@@ -1024,15 +1026,21 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
     std::vector<BoundingBoxf3> lower_bboxes { instances.size() };
 
     for (ModelVolume *volume : volumes) {
+        const auto volume_matrix = volume->get_matrix();
+
         if (! volume->is_model_part()) {
-            // Don't cut modifiers
+            // Modifiers are not cut, but we still need to add the instance transformation
+            // to the modifier volume transformation to preserve their shape properly.
+
+            volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
+
             if (keep_upper) { upper->add_volume(*volume); }
             if (keep_lower) { lower->add_volume(*volume); }
         } else {
             TriangleMesh upper_mesh, lower_mesh;
 
             // Transform the mesh by the combined transformation matrix
-            volume->mesh.transform(instance_matrix * volume->get_matrix());
+            volume->mesh.transform(instance_matrix * volume_matrix);
 
             // Perform cut
             TriangleMeshSlicer tms(&volume->mesh);

From 705ad7fb698286d281d3afec73a87f0e871032e1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 7 Dec 2018 14:10:16 +0100
Subject: [PATCH 10/15] First version of SLA support points generation

---
 src/libslic3r/CMakeLists.txt          |   2 +
 src/libslic3r/PrintConfig.cpp         |  27 +++++
 src/libslic3r/PrintConfig.hpp         |   8 ++
 src/libslic3r/SLA/SLAAutoSupports.cpp | 155 ++++++++++++++++++++++++++
 src/libslic3r/SLA/SLAAutoSupports.hpp |  41 +++++++
 src/libslic3r/TriangleMesh.hpp        |   2 +-
 src/slic3r/GUI/GLGizmo.cpp            |  51 +++++++--
 src/slic3r/GUI/GLGizmo.hpp            |   3 +
 src/slic3r/GUI/Preset.cpp             |   3 +
 src/slic3r/GUI/Tab.cpp                |   5 +
 10 files changed, 288 insertions(+), 9 deletions(-)
 create mode 100644 src/libslic3r/SLA/SLAAutoSupports.cpp
 create mode 100644 src/libslic3r/SLA/SLAAutoSupports.hpp

diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 669efd620..df3ddb6de 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -139,6 +139,8 @@ add_library(libslic3r STATIC
     Rasterizer/Rasterizer.cpp
     SLAPrint.cpp
     SLAPrint.hpp
+    SLA/SLAAutoSupports.hpp
+    SLA/SLAAutoSupports.cpp
     Slicing.cpp
     Slicing.hpp
     SlicingAdaptive.cpp
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index d86ddc25b..4b3b1e80a 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2561,6 +2561,33 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->default_value = new ConfigOptionFloat(5.0);
 
+    def = this->add("support_density_at_horizontal", coInt);
+    def->label = L("Density on horizontal surfaces");
+    def->category = L("Supports");
+    def->tooltip = L("How many support points (approximately) should be placed on horizontal surface.");
+    def->sidetext = L("points per square dm");
+    def->cli = "";
+    def->min = 0;
+    def->default_value = new ConfigOptionInt(500);
+
+    def = this->add("support_density_at_45", coInt);
+    def->label = L("Density on surfaces at 45 degrees");
+    def->category = L("Supports");
+    def->tooltip = L("How many support points (approximately) should be placed on surface sloping at 45 degrees.");
+    def->sidetext = L("points per square dm");
+    def->cli = "";
+    def->min = 0;
+    def->default_value = new ConfigOptionInt(250);
+
+    def = this->add("support_minimal_z", coFloat);
+    def->label = L("Minimal support point height");
+    def->category = L("Supports");
+    def->tooltip = L("No support points will be placed lower than this value from the bottom.");
+    def->sidetext = L("mm");
+    def->cli = "";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(0.f);
+
     def = this->add("pad_enable", coBool);
     def->label = L("Use pad");
     def->category = L("Pad");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index dac2de7cc..9804d72ee 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -955,6 +955,11 @@ public:
     // and the model object's bounding box bottom. Units in mm.
     ConfigOptionFloat support_object_elevation /*= 5.0*/;
 
+    /////// Following options influence automatic support points placement:
+    ConfigOptionInt support_density_at_horizontal;
+    ConfigOptionInt support_density_at_45;
+    ConfigOptionFloat support_minimal_z;
+
     // Now for the base pool (pad) /////////////////////////////////////////////
 
     // Enabling or disabling support creation
@@ -987,6 +992,9 @@ protected:
         OPT_PTR(support_base_height);
         OPT_PTR(support_critical_angle);
         OPT_PTR(support_max_bridge_length);
+        OPT_PTR(support_density_at_horizontal);
+        OPT_PTR(support_density_at_45);
+        OPT_PTR(support_minimal_z);
         OPT_PTR(support_object_elevation);
         OPT_PTR(pad_enable);
         OPT_PTR(pad_wall_thickness);
diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp
new file mode 100644
index 000000000..9b1c56e51
--- /dev/null
+++ b/src/libslic3r/SLA/SLAAutoSupports.cpp
@@ -0,0 +1,155 @@
+#include "igl/random_points_on_mesh.h"
+#include "igl/AABB.h"
+
+#include "SLAAutoSupports.hpp"
+#include "Model.hpp"
+
+#include <iostream>
+
+
+namespace Slic3r {
+
+SLAAutoSupports::SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c)
+: m_model_object(mo), mesh(), m_config(c)
+{}
+
+
+float SLAAutoSupports::approximate_geodesic_distance(const Vec3f& p1, const Vec3f& p2, Vec3f& n1, Vec3f& n2)
+{
+    n1.normalize();
+    n2.normalize();
+
+    Vec3f v = (p2-p1);
+    v.normalize();
+
+    float c1 = n1.dot(v);
+    float c2 = n2.dot(v);
+    float result = pow(p1(0)-p2(0), 2) + pow(p1(1)-p2(1), 2) + pow(p1(2)-p2(2), 2);
+    // Check for division by zero:
+    if(fabs(c1 - c2) > 0.0001)
+        result *= (asin(c1) - asin(c2)) / (c1 - c2);
+    return result;
+}
+
+
+void SLAAutoSupports::generate()
+{
+    // Loads the ModelObject raw_mesh and transforms it by first instance's transformation matrix (disregarding translation).
+    // Instances only differ in z-rotation, so it does not matter which of them will be used for the calculation.
+    // The supports point will be calculated on this mesh (so scaling ang vertical direction is correctly accounted for).
+    // Results will be inverse-transformed to raw_mesh coordinates.
+    TriangleMesh mesh = m_model_object.raw_mesh();
+    Transform3d transformation_matrix = m_model_object.instances[0]->get_matrix(true/*dont_translate*/);
+    mesh.transform(transformation_matrix);
+
+    // Check that the object is thick enough to produce any support points
+    BoundingBoxf3 bb = mesh.bounding_box();
+    if (bb.size()(2) < m_config.minimal_z)
+        return;
+
+    // All points that we curretly have must be transformed too, so distance to them is correcly calculated.
+    for (Vec3f& point : m_model_object.sla_support_points)
+        point = transformation_matrix.cast<float>() * point;
+
+    const stl_file& stl = mesh.stl;
+    Eigen::MatrixXf V;
+    Eigen::MatrixXi F;
+    V.resize(3 * stl.stats.number_of_facets, 3);
+    F.resize(stl.stats.number_of_facets, 3);
+    for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
+        const stl_facet* facet = stl.facet_start+i;
+        V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
+        V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
+        V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
+        F(i, 0) = 3*i+0;
+        F(i, 1) = 3*i+1;
+        F(i, 2) = 3*i+2;
+    }
+
+    // In order to calculate distance to already placed points, we must keep know which facet the point lies on.
+    std::vector<Vec3f> facets_normals;
+
+    // The AABB hierarchy will be used to find normals of already placed points.
+    // The points added automatically will just push_back the new normal on the fly.
+    igl::AABB<Eigen::MatrixXf,3> aabb;
+    aabb.init(V, F);
+    for (unsigned int i=0; i<m_model_object.sla_support_points.size(); ++i) {
+        int facet_idx = 0;
+        Eigen::Matrix<float, 1, 3> dump;
+        Eigen::MatrixXf query_point = m_model_object.sla_support_points[i];
+        aabb.squared_distance(V, F, query_point, facet_idx, dump);
+        Vec3f a1 = V.row(F(facet_idx,1)) - V.row(F(facet_idx,0));
+        Vec3f a2 = V.row(F(facet_idx,2)) - V.row(F(facet_idx,0));
+        Vec3f normal = a1.cross(a2);
+        normal.normalize();
+        facets_normals.push_back(normal);
+    }
+
+    // New potential support point is randomly generated on the mesh and distance to all already placed points is calculated.
+    // In case it is never smaller than certain limit (depends on the new point's facet normal), the point is accepted.
+    // The process stops after certain number of points is refused in a row.
+    Vec3f point;
+    Vec3f normal;
+    int added_points = 0;
+    int refused_points = 0;
+    const int refused_limit = 30;
+    // Angle at which the density reaches zero:
+    const float threshold_angle = std::min(M_PI_2, M_PI_4 * acos(0.f/m_config.density_at_horizontal) / acos(m_config.density_at_45/m_config.density_at_horizontal));
+
+    srand(time(NULL)); // rand() is used by igl::random_point_on_mesh
+    while (refused_points < refused_limit) {
+        // Place a random point on the mesh and calculate corresponding facet's normal:
+        Eigen::VectorXi FI;
+        Eigen::MatrixXf B;
+        igl::random_points_on_mesh(1, V, F, B, FI);
+        point = B(0,0)*V.row(F(FI(0),0)) +
+                B(0,1)*V.row(F(FI(0),1)) +
+                B(0,2)*V.row(F(FI(0),2));
+        if (point(2) - bb.min(2) < m_config.minimal_z)
+            continue;
+
+        Vec3f a1 = V.row(F(FI(0),1)) - V.row(F(FI(0),0));
+        Vec3f a2 = V.row(F(FI(0),2)) - V.row(F(FI(0),0));
+        normal = a1.cross(a2);
+        normal.normalize();
+
+        // calculate angle between the normal and vertical:
+        float angle = angle_from_normal(normal);
+        if (angle > threshold_angle)
+            continue;
+
+        const float distance_limit = 1./(2.4*get_required_density(angle));
+        bool add_it = true;
+        for (unsigned int i=0; i<m_model_object.sla_support_points.size(); ++i) {
+            if (approximate_geodesic_distance(m_model_object.sla_support_points[i], point, facets_normals[i], normal) < distance_limit) {
+                add_it = false;
+                ++refused_points;
+                break;
+            }
+        }
+        if (add_it) {
+            m_model_object.sla_support_points.push_back(point);
+            facets_normals.push_back(normal);
+            ++added_points;
+            refused_points = 0;
+        }
+    }
+
+    // Now transform all support points to mesh coordinates:
+    for (Vec3f& point : m_model_object.sla_support_points)
+        point = transformation_matrix.inverse().cast<float>() * point;
+}
+
+
+
+float SLAAutoSupports::get_required_density(float angle) const
+{
+    // calculation would be density_0 * cos(angle). To provide one more degree of freedom, we will scale the angle
+    // to get the user-set density for 45 deg. So it ends up as density_0 * cos(K * angle).
+    float K = 4*acos(m_config.density_at_45/m_config.density_at_horizontal) / M_PI;
+    return std::max(0., m_config.density_at_horizontal * cos(K*angle));
+}
+
+
+
+} // namespace Slic3r
\ No newline at end of file
diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp
new file mode 100644
index 000000000..637f0909c
--- /dev/null
+++ b/src/libslic3r/SLA/SLAAutoSupports.hpp
@@ -0,0 +1,41 @@
+#ifndef SLAAUTOSUPPORTS_HPP_
+#define SLAAUTOSUPPORTS_HPP_
+
+#include <libslic3r/Point.hpp>
+
+
+namespace Slic3r {
+
+class ModelObject;
+
+
+
+
+class SLAAutoSupports {
+public:
+    struct Config {
+            float density_at_horizontal;
+            float density_at_45;
+            float minimal_z;
+        };
+
+    SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c);
+    void generate();
+
+private:
+    TriangleMesh mesh;
+    static float angle_from_normal(const stl_normal& normal) { return acos((-normal.normalized())(2)); }
+    float get_required_density(float angle) const;
+    static float approximate_geodesic_distance(const Vec3f& p1, const Vec3f& p2, Vec3f& n1, Vec3f& n2);
+
+    ModelObject& m_model_object;
+    SLAAutoSupports::Config m_config;
+};
+
+
+
+
+} // namespace Slic3r
+
+
+#endif // SLAAUTOSUPPORTS_HPP_
\ No newline at end of file
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index e2bf47014..81f64ac85 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -35,7 +35,7 @@ public:
     void repair();
     float volume();
     void check_topology();
-    bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets; }
+    bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
     void WriteOBJFile(char* output_file);
     void scale(float factor);
     void scale(const Vec3d &versor);
diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp
index 239b45702..34de24983 100644
--- a/src/slic3r/GUI/GLGizmo.cpp
+++ b/src/slic3r/GUI/GLGizmo.cpp
@@ -20,6 +20,7 @@
 #include <wx/stattext.h>
 #include <wx/debug.h>
 
+#include "Tab.hpp"
 #include "GUI.hpp"
 #include "GUI_Utils.hpp"
 #include "GUI_App.hpp"
@@ -1079,7 +1080,7 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D::
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
     m_imgui->set_next_window_bg_alpha(0.5f);
     m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
-    m_imgui->input_vec3("", m_scale, 100.0f, "%.2f");
+    m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f");
     m_imgui->end();
 }
 #endif // ENABLE_IMGUI
@@ -1782,8 +1783,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G
 #else
 void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object)
 {
-    if (model_object != nullptr)
-    {
+    if (model_object != nullptr) {
         m_starting_center = Vec3d::Zero();
         m_model_object = model_object;
 
@@ -1812,6 +1812,7 @@ void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const
     Vec3d dragged_offset = selection.get_bounding_box().center() - m_starting_center;
 #endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
 
+
     for (auto& g : m_grabbers) {
         g.color[0] = 1.f;
         g.color[1] = 0.f;
@@ -1871,6 +1872,16 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection,
     float render_color[3];
     for (int i = 0; i < (int)m_grabbers.size(); ++i)
     {
+        // first precalculate the grabber position in world coordinates, so that the grabber
+        // is not scaled with the object (as it would be if rendered with current gl matrix).
+        Eigen::Matrix<GLfloat, 4, 4> glmatrix;
+        glGetFloatv (GL_MODELVIEW_MATRIX, glmatrix.data());
+        Eigen::Matrix<float, 4, 1> grabber_pos;
+        for (int j=0; j<3; ++j)
+            grabber_pos(j) = m_grabbers[i].center(j);
+        grabber_pos[3] = 1.f;
+        Eigen::Matrix<float, 4, 1> grabber_world_position = glmatrix * grabber_pos;
+
         if (!picking && (m_hover_id == i))
         {
             render_color[0] = 1.0f - m_grabbers[i].color[0];
@@ -1882,9 +1893,10 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection,
 
         ::glColor3fv(render_color);
         ::glPushMatrix();
-        ::glTranslated(m_grabbers[i].center(0), m_grabbers[i].center(1), m_grabbers[i].center(2));
+        ::glLoadIdentity();
+        ::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift);
         ::gluQuadricDrawStyle(m_quadric, GLU_SILHOUETTE);
-        ::gluSphere(m_quadric, 0.75, 64, 32);
+        ::gluSphere(m_quadric, 0.75, 64, 36);
         ::glPopMatrix();
     }
 
@@ -1933,7 +1945,7 @@ void GLGizmoSlaSupports::render_grabbers(bool picking) const
             GLUquadricObj *quadric;
             quadric = ::gluNewQuadric();
             ::gluQuadricDrawStyle(quadric, GLU_FILL );
-            ::gluSphere( quadric , 0.75f, 36 , 18 );
+            ::gluSphere( quadric , 0.75f, 64 , 32 );
             ::gluDeleteQuadric(quadric);
             ::glPopMatrix();
             if (!picking)
@@ -1992,6 +2004,7 @@ void GLGizmoSlaSupports::update_mesh()
 
     // we'll now reload Grabbers (selection might have changed):
     m_grabbers.clear();
+
     for (const Vec3f& point : m_model_object->sla_support_points) {
         m_grabbers.push_back(Grabber());
         m_grabbers.back().center = point.cast<double>();
@@ -2083,7 +2096,6 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all)
     if (delete_all) {
         m_grabbers.clear();
         m_model_object->sla_support_points.clear();
-        m_parent.reload_scene(true); // in case this was called from ImGui overlay dialog, the refresh would be delayed
 
         // This should trigger the support generation
         // wxGetApp().plater()->reslice();
@@ -2159,12 +2171,34 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas
     m_imgui->text(_(L("Right mouse click - remove point")));
     m_imgui->text(" ");
 
-    bool remove_all_clicked = m_imgui->button(_(L("Remove all points")));
+    bool generate = m_imgui->button(_(L("Generate points automatically")));
+    bool remove_all_clicked = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_model_object->sla_support_points.size())+")"));
 
     m_imgui->end();
 
     if (remove_all_clicked)
         delete_current_grabber(true);
+
+    if (generate) {
+        const DynamicPrintConfig& cfg = *wxGetApp().get_tab(Preset::TYPE_SLA_PRINT)->get_config();
+        SLAAutoSupports::Config config;
+        config.density_at_horizontal = cfg.opt_int("support_density_at_horizontal") / 10000.f;
+        config.density_at_45 = cfg.opt_int("support_density_at_45") / 10000.f;
+        config.minimal_z = cfg.opt_float("support_minimal_z");
+
+        SLAAutoSupports sas(*m_model_object, config);
+        sas.generate();
+        m_grabbers.clear();
+        for (const Vec3f& point : m_model_object->sla_support_points) {
+            m_grabbers.push_back(Grabber());
+            m_grabbers.back().center = point.cast<double>();
+        }
+    }
+
+    if (remove_all_clicked || generate) {
+        m_parent.reload_scene(true);
+        m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
+    }
 }
 #endif // ENABLE_IMGUI
 
@@ -2180,6 +2214,7 @@ bool GLGizmoSlaSupports::on_is_selectable() const
 }
 
 std::string GLGizmoSlaSupports::on_get_name() const
+
 {
     return L("SLA Support Points");
 }
diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp
index 4229d0362..bfec0965e 100644
--- a/src/slic3r/GUI/GLGizmo.hpp
+++ b/src/slic3r/GUI/GLGizmo.hpp
@@ -5,8 +5,10 @@
 
 #include "../../slic3r/GUI/GLTexture.hpp"
 #include "../../slic3r/GUI/GLCanvas3D.hpp"
+
 #include "libslic3r/Point.hpp"
 #include "libslic3r/BoundingBox.hpp"
+#include "libslic3r/SLA/SLAAutoSupports.hpp"
 
 #include <array>
 #include <vector>
@@ -434,6 +436,7 @@ protected:
 class GLGizmoSlaSupports : public GLGizmoBase
 {
 private:
+    SLAAutoSupports* m_sas = nullptr;
     ModelObject* m_model_object = nullptr;
 #if ENABLE_SLA_SUPPORT_GIZMO_MOD
     ModelObject* m_old_model_object = nullptr;
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index bab3293a4..3100f25a1 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -403,6 +403,9 @@ const std::vector<std::string>& Preset::sla_print_options()
             "support_critical_angle",
             "support_max_bridge_length",
             "support_object_elevation",
+            "support_density_at_horizontal",
+            "support_density_at_45",
+            "support_minimal_z",
             "pad_enable",
             "pad_wall_thickness",
             "pad_wall_height",
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 6d6b3b614..9a5c960bb 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3112,6 +3112,11 @@ void TabSLAPrint::build()
     optgroup->append_single_option_line("support_critical_angle");
     optgroup->append_single_option_line("support_max_bridge_length");
 
+    optgroup = page->new_optgroup(_(L("Automatic generation")));
+    optgroup->append_single_option_line("support_density_at_horizontal");
+    optgroup->append_single_option_line("support_density_at_45");
+    optgroup->append_single_option_line("support_minimal_z");
+
     page = add_options_page(_(L("Pad")), "brick.png");
     optgroup = page->new_optgroup(_(L("Pad")));
     optgroup->append_single_option_line("pad_enable");

From 7743a27325b9f570527864e9dab1e9cb57671d7b Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 7 Dec 2018 14:53:24 +0100
Subject: [PATCH 11/15] Fix of a visual studio compilation issue.

---
 src/libslic3r/SLA/SLAAutoSupports.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp
index 9b1c56e51..ca04b1bee 100644
--- a/src/libslic3r/SLA/SLAAutoSupports.cpp
+++ b/src/libslic3r/SLA/SLAAutoSupports.cpp
@@ -146,8 +146,8 @@ float SLAAutoSupports::get_required_density(float angle) const
 {
     // calculation would be density_0 * cos(angle). To provide one more degree of freedom, we will scale the angle
     // to get the user-set density for 45 deg. So it ends up as density_0 * cos(K * angle).
-    float K = 4*acos(m_config.density_at_45/m_config.density_at_horizontal) / M_PI;
-    return std::max(0., m_config.density_at_horizontal * cos(K*angle));
+    float K = 4.f * float(acos(m_config.density_at_45/m_config.density_at_horizontal) / M_PI);
+    return std::max(0.f, float(m_config.density_at_horizontal * cos(K*angle)));
 }
 
 

From 10cf82774b0e80b4bd4c7f6e81a1535f8fbdd341 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 7 Dec 2018 15:24:50 +0100
Subject: [PATCH 12/15] Fixed OSX build.

---
 src/libslic3r/SLA/SLAAutoSupports.hpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp
index 637f0909c..40726fd0e 100644
--- a/src/libslic3r/SLA/SLAAutoSupports.hpp
+++ b/src/libslic3r/SLA/SLAAutoSupports.hpp
@@ -2,6 +2,7 @@
 #define SLAAUTOSUPPORTS_HPP_
 
 #include <libslic3r/Point.hpp>
+#include <libslic3r/TriangleMesh.hpp>
 
 
 namespace Slic3r {

From 198b23283cf3ec20d2894030b2bf4836d0d44c4b Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 16:23:04 +0100
Subject: [PATCH 13/15] Camera target constrained to scene bounding box

---
 src/libslic3r/Technologies.hpp |  2 +
 src/slic3r/GUI/GLCanvas3D.cpp  | 86 ++++++++++++++++++++++++++++++++--
 src/slic3r/GUI/GLCanvas3D.hpp  | 25 +++++++++-
 3 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index cef3eedd1..618030571 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -31,6 +31,8 @@
 #define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0)
 // Removes the wxNotebook from plater
 #define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0)
+// Constrains the camera target into the scene bounding box
+#define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0)
 
 #endif // _technologies_h_
 
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 1cb8622de..208516536 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -273,8 +273,13 @@ GLCanvas3D::Camera::Camera()
     , zoom(1.0f)
     , phi(45.0f)
 //    , distance(0.0f)
+#if !ENABLE_CONSTRAINED_CAMERA_TARGET
     , target(0.0, 0.0, 0.0)
+#endif // !ENABLE_CONSTRAINED_CAMERA_TARGET
     , m_theta(45.0f)
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    , m_target(Vec3d::Zero())
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
 {
 }
 
@@ -292,16 +297,32 @@ std::string GLCanvas3D::Camera::get_type_as_string() const
     };
 }
 
-float GLCanvas3D::Camera::get_theta() const
-{
-    return m_theta;
-}
-
 void GLCanvas3D::Camera::set_theta(float theta)
 {
     m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta);
 }
 
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+void GLCanvas3D::Camera::set_target(const Vec3d& target, GLCanvas3D& canvas)
+{
+    m_target = target;
+    m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0));
+    m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1));
+    m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2));
+    if (!m_target.isApprox(target))
+        canvas.viewport_changed();
+}
+
+void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas)
+{
+    if (m_scene_box != box)
+    {
+        m_scene_box = box;
+        canvas.viewport_changed();
+    }
+}
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
+
 GLCanvas3D::Bed::Bed()
     : m_type(Custom)
 {
@@ -3650,6 +3671,15 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
     return bb;
 }
 
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
+{
+    BoundingBoxf3 bb = volumes_bounding_box();
+    bb.merge(m_bed.get_bounding_box());
+    return bb;
+}
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
+
 bool GLCanvas3D::is_layers_editing_enabled() const
 {
     return m_layers_editing.is_enabled();
@@ -3789,7 +3819,12 @@ void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other)
 {
     m_camera.phi = other.m_camera.phi;
     m_camera.set_theta(other.m_camera.get_theta());
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    m_camera.set_scene_box(other.m_camera.get_scene_box(), *this);
+    m_camera.set_target(other.m_camera.get_target(), *this);
+#else
     m_camera.target = other.m_camera.target;
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
     m_camera.zoom = other.m_camera.zoom;
     m_dirty = true;
 }
@@ -4318,6 +4353,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
 
     // restore to default value
     m_regenerate_volumes = true;
+
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    m_camera.set_scene_box(scene_bounding_box(), *this);
+    m_camera.set_target(m_camera.get_target(), *this);
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
+
     // and force this canvas to be redrawn.
     m_dirty = true;
 }
@@ -4857,7 +4898,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                 float z = 0.0f;
                 const Vec3d& cur_pos = _mouse_to_3d(pos, &z);
                 Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z);
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+                m_camera.set_target(m_camera.get_target() + orig - cur_pos, *this);
+#else
                 m_camera.target += orig - cur_pos;
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
 
                 viewport_changed();
 
@@ -4938,10 +4983,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 #if ENABLE_WORLD_ROTATIONS
             _update_gizmos_data();
 #endif // ENABLE_WORLD_ROTATIONS
+
             wxGetApp().obj_manipul()->update_settings_value(m_selection);
             // Let the platter know that the dragging finished, so a delayed refresh
             // of the scene with the background processing data should be performed.
             post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+            m_camera.set_scene_box(scene_bounding_box(), *this);
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
         }
 
         m_moving = false;
@@ -5533,7 +5582,11 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox)
     {
         m_camera.zoom = zoom;
         // center view around bounding box center
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+        m_camera.set_target(bbox.center(), *this);
+#else
         m_camera.target = bbox.center();
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
 
         viewport_changed();
 
@@ -5655,7 +5708,12 @@ void GLCanvas3D::_camera_tranform() const
     ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
     ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f);          // yaw
 
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    Vec3d target = -m_camera.get_target();
+    ::glTranslated(target(0), target(1), target(2));
+#else
     ::glTranslated(-m_camera.target(0), -m_camera.target(1), -m_camera.target(2));
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
 }
 
 void GLCanvas3D::_picking_pass() const
@@ -5970,6 +6028,23 @@ void GLCanvas3D::_render_camera_target() const
 
     ::glLineWidth(2.0f);
     ::glBegin(GL_LINES);
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    const Vec3d& target = m_camera.get_target();
+    // draw line for x axis
+    ::glColor3f(1.0f, 0.0f, 0.0f);
+    ::glVertex3d(target(0) - half_length, target(1), target(2));
+    ::glVertex3d(target(0) + half_length, target(1), target(2));
+    // draw line for y axis
+    ::glColor3f(0.0f, 1.0f, 0.0f);
+    ::glVertex3d(target(0), target(1) - half_length, target(2));
+    ::glVertex3d(target(0), target(1) + half_length, target(2));
+    ::glEnd();
+
+    ::glBegin(GL_LINES);
+    ::glColor3f(0.0f, 0.0f, 1.0f);
+    ::glVertex3d(target(0), target(1), target(2) - half_length);
+    ::glVertex3d(target(0), target(1), target(2) + half_length);
+#else
     // draw line for x axis
     ::glColor3f(1.0f, 0.0f, 0.0f);
     ::glVertex3d(m_camera.target(0) - half_length, m_camera.target(1), m_camera.target(2));
@@ -5984,6 +6059,7 @@ void GLCanvas3D::_render_camera_target() const
     ::glColor3f(0.0f, 0.0f, 1.0f);
     ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) - half_length);
     ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) + half_length);
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
     ::glEnd();
 }
 #endif // ENABLE_SHOW_CAMERA_TARGET
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 837b83e8a..d43fff271 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -153,9 +153,15 @@ class GLCanvas3D
         float zoom;
         float phi;
 //        float distance;
+#if !ENABLE_CONSTRAINED_CAMERA_TARGET
         Vec3d target;
+#endif !// ENABLE_CONSTRAINED_CAMERA_TARGET
 
     private:
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+        Vec3d m_target;
+        BoundingBoxf3 m_scene_box;
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
         float m_theta;
 
     public:
@@ -163,8 +169,16 @@ class GLCanvas3D
 
         std::string get_type_as_string() const;
 
-        float get_theta() const;
+        float get_theta() const { return m_theta; }
         void set_theta(float theta);
+
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+        const Vec3d& get_target() const { return m_target; }
+        void set_target(const Vec3d& target, GLCanvas3D& canvas);
+
+        const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
+        void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas);
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
     };
 
     class Bed
@@ -782,7 +796,9 @@ private:
     wxWindow *m_external_gizmo_widgets_parent;
 #endif // not ENABLE_IMGUI
 
+#if !ENABLE_CONSTRAINED_CAMERA_TARGET
     void viewport_changed();
+#endif // !ENABLE_CONSTRAINED_CAMERA_TARGET
 
 public:
     GLCanvas3D(wxGLCanvas* canvas);
@@ -845,6 +861,9 @@ public:
     float get_camera_zoom() const;
 
     BoundingBoxf3 volumes_bounding_box() const;
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    BoundingBoxf3 scene_bounding_box() const;
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
 
     bool is_layers_editing_enabled() const;
     bool is_layers_editing_allowed() const;
@@ -936,6 +955,10 @@ public:
 
     void update_gizmos_on_off_state();
 
+#if ENABLE_CONSTRAINED_CAMERA_TARGET
+    void viewport_changed();
+#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
+
 private:
     bool _is_shown_on_screen() const;
     void _force_zoom_to_bed();

From 45f44da3b11c226fd86bb9a0d64aaf3ebad7c1b2 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 16:44:18 +0100
Subject: [PATCH 14/15] Fixed volume shown in info panel

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

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 2a99d2c5c..bafa24aa0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -733,7 +733,7 @@ void Sidebar::show_info_sizer()
 
     auto& stats = model_object->volumes.front()->mesh.stl.stats;
     auto sf = model_instance->get_scaling_factor();
-    p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume * sf(0) * sf(1) * sf(2)));
+    p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2)));
     p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
 
     int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +

From 967afa87175686f08242098db4b1d86af8ec38f8 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 7 Dec 2018 16:57:43 +0100
Subject: [PATCH 15/15] Disabled imgui dialogs for move/scale/rotate gizmos

---
 src/libslic3r/Technologies.hpp | 1 +
 src/slic3r/GUI/GLGizmo.cpp     | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 618030571..2bd3e2b2a 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -27,6 +27,7 @@
 #define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0)
 // Scene's GUI made using imgui library
 #define ENABLE_IMGUI (1 && ENABLE_1_42_0)
+#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
 // Modified Sla support gizmo
 #define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0)
 // Removes the wxNotebook from plater
diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp
index 34de24983..119838dad 100644
--- a/src/slic3r/GUI/GLGizmo.cpp
+++ b/src/slic3r/GUI/GLGizmo.cpp
@@ -770,6 +770,7 @@ void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const
 #if ENABLE_IMGUI
 void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
 {
+#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
     Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle()));
     wxString label = _(L("Rotation (deg)"));
 
@@ -778,6 +779,7 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D:
     m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
     m_imgui->input_vec3("", rotation, 100.0f, "%.2f");
     m_imgui->end();
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
 }
 #endif // ENABLE_IMGUI
 
@@ -1074,6 +1076,7 @@ void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selectio
 #if ENABLE_IMGUI
 void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
 {
+#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
     bool single_instance = selection.is_single_full_instance();
     wxString label = _(L("Scale (%)"));
 
@@ -1082,6 +1085,7 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D::
     m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
     m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f");
     m_imgui->end();
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
 }
 #endif // ENABLE_IMGUI
 
@@ -1321,6 +1325,7 @@ void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection
 #if ENABLE_IMGUI
 void GLGizmoMove3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
 {
+#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
     bool show_position = selection.is_single_full_instance();
     const Vec3d& position = selection.get_bounding_box().center();
 
@@ -1333,6 +1338,7 @@ void GLGizmoMove3D::on_render_input_window(float x, float y, const GLCanvas3D::S
     m_imgui->input_vec3("", displacement, 100.0f, "%.2f");
 
     m_imgui->end();
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
 }
 #endif // ENABLE_IMGUI