From e664100bf62ceace6b0267523cf4da81d51e990b Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 10 Feb 2021 20:31:17 +0100
Subject: [PATCH] SPE-1103 Added menu items for the conversation of the volumes
 from/to meters Related to #4521

---
 src/libslic3r/Format/3mf.cpp      |  5 +++
 src/libslic3r/Format/AMF.cpp      |  5 +++
 src/libslic3r/Model.cpp           | 22 +++++++++--
 src/libslic3r/Model.hpp           | 14 ++++++-
 src/slic3r/GUI/GUI_ObjectList.cpp | 63 ++++++++++++++++---------------
 src/slic3r/GUI/GUI_ObjectList.hpp |  2 +-
 src/slic3r/GUI/Plater.cpp         | 12 ++++--
 src/slic3r/GUI/Plater.hpp         |  3 +-
 8 files changed, 84 insertions(+), 42 deletions(-)

diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 7108b677f..064a05eb3 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -115,6 +115,7 @@ static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
 static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
 static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
 static constexpr const char* SOURCE_IN_INCHES    = "source_in_inches";
+static constexpr const char* SOURCE_IN_METERS    = "source_in_meters";
 
 const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
 const char* VALID_OBJECT_TYPES[] =
@@ -1913,6 +1914,8 @@ namespace Slic3r {
                     volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
                 else if (metadata.key == SOURCE_IN_INCHES)
                     volume->source.is_converted_from_inches = metadata.value == "1";
+                else if (metadata.key == SOURCE_IN_METERS)
+                    volume->source.is_converted_from_meters = metadata.value == "1";
                 else
                     volume->config.set_deserialize(metadata.key, metadata.value);
             }
@@ -2822,6 +2825,8 @@ namespace Slic3r {
                                 }
                                 if (volume->source.is_converted_from_inches)
                                     stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+                                if (volume->source.is_converted_from_meters)
+                                    stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n";
                             }
 
                             // stores volume's config data
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 9b71990ea..1c9b6b27d 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -798,6 +798,9 @@ void AMFParserContext::endElement(const char * /* name */)
                 else if (strcmp(opt_key, "source_in_inches") == 0) {
                     m_volume->source.is_converted_from_inches = m_value[1] == "1";
                 }
+                else if (strcmp(opt_key, "source_in_meters") == 0) {
+                    m_volume->source.is_converted_from_meters = m_value[1] == "1";
+                }
             }
         } else if (m_path.size() == 3) {
             if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -1232,6 +1235,8 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
             }
             if (volume->source.is_converted_from_inches)
                 stream << "        <metadata type=\"slic3r.source_in_inches\">1</metadata>\n";
+            if (volume->source.is_converted_from_meters)
+                stream << "        <metadata type=\"slic3r.source_in_meters\">1</metadata>\n";
 			stream << std::setprecision(std::numeric_limits<float>::max_digits10);
             const indexed_triangle_set &its = volume->mesh().its;
             for (size_t i = 0; i < its.indices.size(); ++i) {
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 807221964..5f806955e 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -491,7 +491,7 @@ void Model::convert_from_meters(bool only_small_volumes)
         if (! only_small_volumes || obj->get_object_stl_stats().volume < 0.001) { // 0.001 = 0.1*0.1*0.1;
             obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
             for (ModelVolume* v : obj->volumes)
-                v->source.is_converted_from_inches = true;
+                v->source.is_converted_from_meters = true;
         }
 }
 
@@ -1046,13 +1046,14 @@ void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
     this->invalidate_bounding_box();
 }
 
-void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial, std::vector<int> volume_idxs)
+void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType conv_type, std::vector<int> volume_idxs)
 {
     BOOST_LOG_TRIVIAL(trace) << "ModelObject::convert_units - start";
 
     ModelObject* new_object = new_clone(*this);
 
-    double koef = from_imperial ? 25.4 : 0.0393700787;
+    double koef = conv_type == ConversionType::CONV_FROM_INCH   ? 25.4 : conv_type == ConversionType::CONV_TO_INCH  ? 0.0393700787  :
+                  conv_type == ConversionType::CONV_FROM_METER  ? 1000 : conv_type == ConversionType::CONV_TO_METER ? 0.001         : 1;
     const Vec3d versor = Vec3d(koef, koef, koef);
 
     new_object->set_model(nullptr);
@@ -1080,6 +1081,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
             vol->source.input_file = volume->source.input_file;
             vol->source.object_idx = (int)new_objects.size();
             vol->source.volume_idx = vol_idx;
+            vol->source.is_converted_from_inches = volume->source.is_converted_from_inches;
+            vol->source.is_converted_from_meters = volume->source.is_converted_from_meters;
 
             vol->supported_facets.assign(volume->supported_facets);
             vol->seam_facets.assign(volume->seam_facets);
@@ -1091,7 +1094,10 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
                  std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
                 vol->scale_geometry_after_creation(versor);
                 vol->set_offset(versor.cwiseProduct(volume->get_offset()));
-                vol->source.is_converted_from_inches = from_imperial;
+                if (conv_type == ConversionType::CONV_FROM_INCH || conv_type == ConversionType::CONV_TO_INCH)
+                    vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH;
+                if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER)
+                    vol->source.is_converted_from_meters = conv_type == ConversionType::CONV_FROM_METER;
             }
             else
                 vol->set_offset(volume->get_offset());
@@ -1841,6 +1847,14 @@ void ModelVolume::convert_from_imperial_units()
     this->source.is_converted_from_inches = true;
 }
 
+void ModelVolume::convert_from_meters()
+{
+    double m_to_mm = 1000;
+    this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
+    this->set_offset(Vec3d(0, 0, 0));
+    this->source.is_converted_from_meters = true;
+}
+
 void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
 {
     mesh->transform(get_matrix(dont_translate));
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index b06ecf527..868639ee8 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -29,6 +29,7 @@ namespace cereal {
 }
 
 namespace Slic3r {
+enum class ConversionType;
 
 class Model;
 class ModelInstance;
@@ -325,7 +326,7 @@ public:
 
     // This method could only be called before the meshes of this ModelVolumes are not shared!
     void scale_mesh_after_creation(const Vec3d& versor);
-    void convert_units(ModelObjectPtrs&new_objects, bool from_imperial, std::vector<int> volume_idxs);
+    void convert_units(ModelObjectPtrs&new_objects, ConversionType conv_type, std::vector<int> volume_idxs);
 
     size_t materials_count() const;
     size_t facets_count() const;
@@ -495,6 +496,13 @@ enum class EnforcerBlockerType : int8_t {
     BLOCKER   = 2
 };
 
+enum class ConversionType : int {
+    CONV_TO_INCH,
+    CONV_FROM_INCH,
+    CONV_TO_METER,
+    CONV_FROM_METER,
+};
+
 class FacetsAnnotation final : public ObjectWithTimestamp {
 public:
     // Assign the content if the timestamp differs, don't assign an ObjectID.
@@ -553,11 +561,12 @@ public:
         Vec3d mesh_offset{ Vec3d::Zero() };
         Geometry::Transformation transform;
         bool is_converted_from_inches = false;
+        bool is_converted_from_meters = false;
 
         template<class Archive> void serialize(Archive& ar) { 
             //FIXME Vojtech: Serialize / deserialize only if the Source is set.
             // likely testing input_file or object_idx would be sufficient.
-            ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches);
+            ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters);
         }
     };
     Source              source;
@@ -657,6 +666,7 @@ public:
     void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
     void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
     void convert_from_imperial_units();
+    void convert_from_meters();
 
     const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
 
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 3f8d8fa73..b06821bb0 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -1023,7 +1023,7 @@ void ObjectList::show_context_menu(const bool evt_context_menu)
                        printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
 
         if (type & (itObject | itVolume))
-            append_menu_item_convert_unit(menu);
+            append_menu_items_convert_unit(menu);
         if (!(type & itInstance))
             append_menu_item_settings(menu);
     }
@@ -1906,56 +1906,59 @@ void ObjectList::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* me
         [](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu);
 }
 
-void ObjectList::append_menu_item_convert_unit(wxMenu* menu, int insert_pos/* = 1*/)
+void ObjectList::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/)
 {
     std::vector<int> obj_idxs, vol_idxs;
     get_selection_indexes(obj_idxs, vol_idxs);
     if (obj_idxs.empty() && vol_idxs.empty())
         return;
 
-    auto can_append = [this, obj_idxs, vol_idxs](bool from_imperial_unit) {
+    auto volume_respects_conversion = [](ModelVolume* volume, ConversionType conver_type)
+    {
+        return  (conver_type == ConversionType::CONV_FROM_INCH  &&  volume->source.is_converted_from_inches) ||
+                (conver_type == ConversionType::CONV_TO_INCH    && !volume->source.is_converted_from_inches) ||
+                (conver_type == ConversionType::CONV_FROM_METER &&  volume->source.is_converted_from_meters) ||
+                (conver_type == ConversionType::CONV_TO_METER   && !volume->source.is_converted_from_meters);
+    };
+
+    auto can_append = [this, obj_idxs, vol_idxs, volume_respects_conversion](ConversionType conver_type)
+    {
         ModelObjectPtrs objects;
         for (int obj_idx : obj_idxs) {
             ModelObject* object = (*m_objects)[obj_idx];
             if (vol_idxs.empty()) {
                 for (ModelVolume* volume : object->volumes)
-                    if (volume->source.is_converted_from_inches == from_imperial_unit)
+                    if (volume_respects_conversion(volume, conver_type))
                         return false;
             }
             else {
                 for (int vol_idx : vol_idxs)
-                    if (object->volumes[vol_idx]->source.is_converted_from_inches == from_imperial_unit)
+                    if (volume_respects_conversion(object->volumes[vol_idx], conver_type))
                         return false;
             }
         }
         return true;
     };
 
-    wxString convert_menu_name = _L("Convert from imperial units");
-    int      convert_menu_id   = menu->FindItem(convert_menu_name);
-    wxString revert_menu_name  = _L("Revert conversion from imperial units");
-    int      revert_menu_id    = menu->FindItem(revert_menu_name);
+    std::vector<std::pair<ConversionType, wxString>> items = {
+        {ConversionType::CONV_FROM_INCH , _L("Convert from imperial units") },
+        {ConversionType::CONV_TO_INCH   , _L("Revert conversion from imperial units") },
+        {ConversionType::CONV_FROM_METER, _L("Convert from meters") },
+        {ConversionType::CONV_TO_METER  , _L("Revert conversion from meters") } };
 
-    if (can_append(true)) {
-        // Delete revert menu item
-        if (revert_menu_id != wxNOT_FOUND)
-            menu->Destroy(revert_menu_id);
-        // Add convert menu item if it doesn't exist
-        if (convert_menu_id == wxNOT_FOUND)
-            append_menu_item(menu, wxID_ANY, convert_menu_name, convert_menu_name,
-                [](wxCommandEvent&) { wxGetApp().plater()->convert_unit(true); }, "", menu, 
-                []() {return true;}, nullptr, insert_pos);
-    }
-
-    if (can_append(false)) {
-        // Delete convert menu item
-        if (convert_menu_id != wxNOT_FOUND)
-            menu->Destroy(convert_menu_id);
-        // Add convert menu item if it doesn't exist
-        if (revert_menu_id == wxNOT_FOUND)
-            append_menu_item(menu, wxID_ANY, revert_menu_name, revert_menu_name,
-                [](wxCommandEvent&) { wxGetApp().plater()->convert_unit(false); }, "", menu,
-                []() {return true;}, nullptr, insert_pos);
+    for (auto item : items) {
+        int menu_id = menu->FindItem(item.second);
+        if (can_append(item.first)) {
+            // Add menu item if it doesn't exist
+            if (menu_id == wxNOT_FOUND)
+                append_menu_item(menu, wxID_ANY, item.second, item.second,
+                    [item](wxCommandEvent&) { wxGetApp().plater()->convert_unit(item.first); }, "", menu,
+                    []() {return true; }, nullptr, insert_pos);
+        }
+        else if (menu_id != wxNOT_FOUND) {
+            // Delete menu item
+            menu->Destroy(menu_id);
+        }
     }
 }
 
@@ -4606,7 +4609,7 @@ void ObjectList::show_multi_selection_menu()
         return wxGetApp().plater()->can_reload_from_disk();
     }, wxGetApp().plater());
 
-    append_menu_item_convert_unit(menu);
+    append_menu_items_convert_unit(menu);
     if (can_merge_to_multipart_object())
         append_menu_item_merge_to_multipart_object(menu);
 
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 15d5ecb08..0846def53 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -284,7 +284,7 @@ public:
     void                append_menu_item_change_extruder(wxMenu* menu);
     void                append_menu_item_delete(wxMenu* menu);
     void                append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
-    void                append_menu_item_convert_unit(wxMenu* menu, int insert_pos = 1); // Add "Conver/Revert..." menu item after "Reload From Disk"
+    void                append_menu_items_convert_unit(wxMenu* menu, int insert_pos = 1); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk"
     void                append_menu_item_merge_to_multipart_object(wxMenu *menu);
     void                append_menu_item_merge_to_single_object(wxMenu *menu);
     void                create_object_popupmenu(wxMenu *menu);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index b6dda66fd..5296fd6fa 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3329,6 +3329,8 @@ void Plater::priv::reload_from_disk()
                     new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
                     if (old_volume->source.is_converted_from_inches)
                         new_volume->convert_from_imperial_units();
+                    if (old_volume->source.is_converted_from_meters)
+                        new_volume->convert_from_meters();
                     new_volume->supported_facets.assign(old_volume->supported_facets);
                     new_volume->seam_facets.assign(old_volume->seam_facets);
                     std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
@@ -3871,7 +3873,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
                 menu_item_convert_unit_position = 2;
         }
 
-        sidebar->obj_list()->append_menu_item_convert_unit(menu, menu_item_convert_unit_position);
+        sidebar->obj_list()->append_menu_items_convert_unit(menu, menu_item_convert_unit_position);
         sidebar->obj_list()->append_menu_item_settings(menu);
 
         if (printer_technology != ptSLA)
@@ -5246,20 +5248,22 @@ void Plater::scale_selection_to_fit_print_volume()
     p->scale_selection_to_fit_print_volume();
 }
 
-void Plater::convert_unit(bool from_imperial_unit)
+void Plater::convert_unit(ConversionType conv_type)
 {
     std::vector<int> obj_idxs, volume_idxs;
     wxGetApp().obj_list()->get_selection_indexes(obj_idxs, volume_idxs);
     if (obj_idxs.empty() && volume_idxs.empty())
         return;
 
-    TakeSnapshot snapshot(this, from_imperial_unit ? _L("Convert from imperial units") : _L("Revert conversion from imperial units"));
+    TakeSnapshot snapshot(this, conv_type == ConversionType::CONV_FROM_INCH  ? _L("Convert from imperial units") :
+                                conv_type == ConversionType::CONV_TO_INCH    ? _L("Revert conversion from imperial units") :
+                                conv_type == ConversionType::CONV_FROM_METER ? _L("Convert from meters") : _L("Revert conversion from meters"));
     wxBusyCursor wait;
 
     ModelObjectPtrs objects;
     for (int obj_idx : obj_idxs) {
         ModelObject *object = p->model.objects[obj_idx];
-        object->convert_units(objects, from_imperial_unit, volume_idxs);
+        object->convert_units(objects, conv_type, volume_idxs);
         remove(obj_idx);
     }
     p->load_model_objects(objects);
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 5407a6e85..f40acc215 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -28,6 +28,7 @@ class ModelInstance;
 class Print;
 class SLAPrint;
 enum SLAPrintObjectStep : unsigned int;
+enum class ConversionType : int;
 
 using ModelInstancePtrs = std::vector<ModelInstance*>;
 
@@ -191,7 +192,7 @@ public:
     void fill_bed_with_instances();
     bool is_selection_empty() const;
     void scale_selection_to_fit_print_volume();
-    void convert_unit(bool from_imperial_unit);
+    void convert_unit(ConversionType conv_type);
 
     void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);