From fd509199dab93aea2806e5230f4f9102278b5bc0 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Wed, 24 Nov 2021 10:55:48 +0100
Subject: [PATCH] Tech ENABLE_ENHANCED_PRINT_VOLUME_FIT - 1st installment,
 Scale to print volume command for circular printbeds

---
 src/libslic3r/Technologies.hpp   |  3 ++
 src/slic3r/GUI/GUI_Factories.cpp |  6 +++
 src/slic3r/GUI/Plater.cpp        | 19 +++++++
 src/slic3r/GUI/Plater.hpp        |  3 ++
 src/slic3r/GUI/Selection.cpp     | 92 ++++++++++++++++++++++++++++++++
 src/slic3r/GUI/Selection.hpp     |  7 +++
 6 files changed, 130 insertions(+)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 39f1fa4c7..3b288198e 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -91,5 +91,8 @@
 // an additional button can be used to set the keyboard focus into the slider
 // to allow the user to type in the desired value
 #define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
+// Enable fit print volume command for circular printbeds
+#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2)
+
 
 #endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index 3671ee130..588d02cbc 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -786,8 +786,14 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
 
 void MenuFactory::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu)
 {
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+    append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"),
+        [](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu,
+        []() { return plater()->can_scale_to_print_volume(); }, m_parent);
+#else
     append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"),
         [](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu);
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 }
 
 void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/)
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 7cbaa9d08..6075cdc04 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1910,6 +1910,9 @@ struct Plater::priv
     bool can_reload_from_disk() const;
     bool can_replace_with_stl() const;
     bool can_split(bool to_objects) const;
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+    bool can_scale_to_print_volume() const;
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 
     void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type);
     ThumbnailsList generate_thumbnails(const ThumbnailsParams& params, Camera::EType camera_type);
@@ -3075,7 +3078,11 @@ void Plater::priv::split_volume()
 
 void Plater::priv::scale_selection_to_fit_print_volume()
 {
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+    this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(this->bed.build_volume());
+#else
     this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(*config);
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 }
 
 void Plater::priv::schedule_background_process()
@@ -4543,6 +4550,14 @@ bool Plater::priv::can_split(bool to_objects) const
     return sidebar->obj_list()->is_splittable(to_objects);
 }
 
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+bool Plater::priv::can_scale_to_print_volume() const
+{
+    const BuildVolume::Type type = this->bed.build_volume().type();
+    return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle);
+}
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
+
 bool Plater::priv::layers_height_allowed() const
 {
     if (printer_technology != ptFFF)
@@ -6870,6 +6885,10 @@ bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
 bool Plater::can_replace_with_stl() const { return p->can_replace_with_stl(); }
 bool Plater::can_mirror() const { return p->can_mirror(); }
 bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+bool Plater::can_scale_to_print_volume() const { return p->can_scale_to_print_volume(); }
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
+
 const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
 void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
 void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 19bd67eda..55a36d21c 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -322,6 +322,9 @@ public:
     bool can_replace_with_stl() const;
     bool can_mirror() const;
     bool can_split(bool to_objects) const;
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+    bool can_scale_to_print_volume() const;
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 
     void msw_rescale();
     void sys_color_changed();
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index c3313d434..c596b0c2c 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -15,6 +15,9 @@
 #include "libslic3r/LocalesUtils.hpp"
 #include "libslic3r/Model.hpp"
 #include "libslic3r/PresetBundle.hpp"
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+#include "libslic3r/BuildVolume.hpp"
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 
 #include <GL/glew.h>
 
@@ -948,6 +951,94 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
     wxGetApp().plater()->canvas3D()->requires_check_outside_state();
 }
 
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
+{
+    auto fit = [this](double s, const Vec3d& offset) {
+        if (s <= 0.0 || s == 1.0)
+            return;
+
+        wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
+
+        TransformationType type;
+        type.set_world();
+        type.set_relative();
+        type.set_joint();
+
+        // apply scale
+        start_dragging();
+        scale(s * Vec3d::Ones(), type);
+        wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot
+
+        // center selection on print bed
+        start_dragging();
+        translate(offset);
+        wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
+
+        wxGetApp().obj_manipul()->set_dirty();
+    };
+
+    auto fit_rectangle = [this, fit](const BuildVolume& volume) {
+        const BoundingBoxf3 print_volume = volume.bounding_volume();
+        const Vec3d print_volume_size = print_volume.size();
+
+        // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
+        const Vec3d box_size = get_bounding_box().size() + 0.02 * Vec3d::Ones();
+
+        const double sx = (box_size.x() != 0.0) ? print_volume_size.x() / box_size.x() : 0.0;
+        const double sy = (box_size.y() != 0.0) ? print_volume_size.y() / box_size.y() : 0.0;
+        const double sz = (box_size.z() != 0.0) ? print_volume_size.z() / box_size.z() : 0.0;
+
+        if (sx != 0.0 && sy != 0.0 && sz != 0.0)
+            fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center());
+    };
+
+    auto fit_circle = [this, fit](const BuildVolume& volume) {
+        const Geometry::Circled& print_circle = volume.circle();
+        double print_circle_radius = unscale<double>(print_circle.radius);
+
+        if (print_circle_radius == 0.0)
+            return;
+
+        Points points;
+        double max_z = 0.0;
+        for (unsigned int i : m_list) {
+            const GLVolume& v = *(*m_volumes)[i];
+            TriangleMesh hull_3d = *v.convex_hull();
+            hull_3d.transform(v.world_matrix());
+            max_z = std::max(max_z, hull_3d.bounding_box().size().z());
+            const Polygon hull_2d = hull_3d.convex_hull();
+            points.insert(points.end(), hull_2d.begin(), hull_2d.end());
+        }
+
+        if (points.empty())
+            return;
+
+        const Geometry::Circled circle = Geometry::smallest_enclosing_circle_welzl(points);
+        // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
+        const double circle_radius = unscale<double>(circle.radius) + 0.01;
+
+        if (circle_radius == 0.0 || max_z == 0.0)
+            return;
+
+        const double s = std::min(print_circle_radius / circle_radius, volume.max_print_height() / max_z);
+        const Vec3d sel_center = get_bounding_box().center();
+        const Vec3d offset = s * (Vec3d(unscale<double>(circle.center.x()), unscale<double>(circle.center.y()), 0.5 * max_z) - sel_center);
+        const Vec3d print_center = { unscale<double>(print_circle.center.x()), unscale<double>(print_circle.center.y()), 0.5 * volume.max_print_height() };
+        fit(s, print_center - (sel_center + offset));
+    };
+
+    if (is_empty() || m_mode == Volume)
+        return;
+
+    switch (volume.type())
+    {
+    case BuildVolume::Type::Rectangle: { fit_rectangle(volume); break; }
+    case BuildVolume::Type::Circle:    { fit_circle(volume); break; }
+    default: { break; }
+    }
+}
+#else
 void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
 {
     if (is_empty() || m_mode == Volume)
@@ -990,6 +1081,7 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
         }
     }
 }
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 
 void Selection::mirror(Axis axis)
 {
diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp
index c81fa0510..c18d24eab 100644
--- a/src/slic3r/GUI/Selection.hpp
+++ b/src/slic3r/GUI/Selection.hpp
@@ -17,6 +17,9 @@ class GLArrow;
 class GLCurvedArrow;
 class DynamicPrintConfig;
 class GLShaderProgram;
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+class BuildVolume;
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
 
 using GLVolumePtrs = std::vector<GLVolume*>;
 using ModelObjectPtrs = std::vector<ModelObject*>;
@@ -320,7 +323,11 @@ public:
     void rotate(const Vec3d& rotation, TransformationType transformation_type);
     void flattening_rotate(const Vec3d& normal);
     void scale(const Vec3d& scale, TransformationType transformation_type);
+#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
+    void scale_to_fit_print_volume(const BuildVolume& volume);
+#else
     void scale_to_fit_print_volume(const DynamicPrintConfig& config);
+#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
     void mirror(Axis axis);
 
     void translate(unsigned int object_idx, const Vec3d& displacement);