From ee1942e4e96d424c2c2009660e34bfdc87f5163e Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Wed, 27 May 2020 10:55:48 +0200 Subject: [PATCH] Implemented merge of the objects to the one multi-part object + Implemented merge of the parts to the one object. But now this function doesn't used. + Fixed Model::looks_like_imperial_units() --- src/libslic3r/Model.cpp | 40 +++++--- src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/GUI_ObjectList.cpp | 147 ++++++++++++++++++++++++++++++ src/slic3r/GUI/GUI_ObjectList.hpp | 5 + src/slic3r/GUI/Plater.cpp | 6 +- 5 files changed, 184 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e940b842..d75af4ab9 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -453,24 +453,19 @@ bool Model::looks_like_imperial_units() const if (this->objects.size() == 0) return false; - stl_vertex size = this->objects[0]->get_object_stl_stats().size; + for (ModelObject* obj : this->objects) + if (obj->get_object_stl_stats().volume < 9.0) // 9 = 3*3*3; + return true; - for (ModelObject* o : this->objects) { - auto sz = o->get_object_stl_stats().size; - - if (size[0] < sz[0]) size[0] = sz[0]; - if (size[1] < sz[1]) size[1] = sz[1]; - if (size[2] < sz[2]) size[2] = sz[2]; - } - - return (size[0] < 3 && size[1] < 3 && size[2] < 3); + return false; } void Model::convert_from_imperial_units() { double in_to_mm = 25.4; - for (ModelObject* o : this->objects) - o->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm)); + for (ModelObject* obj : this->objects) + if (obj->get_object_stl_stats().volume < 9.0) // 9 = 3*3*3; + obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm)); } void Model::adjust_min_z() @@ -1271,6 +1266,27 @@ void ModelObject::split(ModelObjectPtrs* new_objects) return; } +void ModelObject::merge() +{ + if (this->volumes.size() == 1) { + // We can't merge meshes if there's just one volume + return; + } + + TriangleMesh mesh; + + for (ModelVolume* volume : volumes) + if (!volume->mesh().empty()) + mesh.merge(volume->mesh()); + mesh.repair(); + + this->clear_volumes(); + ModelVolume* vol = this->add_volume(mesh); + + if (!vol) + return; +} + // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // This situation is solved by baking in the instance transformation into the mesh vertices. diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index f7b63bff4..e5930fb8a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -287,6 +287,7 @@ public: bool needed_repair() const; ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates void split(ModelObjectPtrs* new_objects); + void merge(); // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // This situation is solved by baking in the instance transformation into the mesh vertices. diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 35ec2e485..b944f5ec6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1781,6 +1781,22 @@ void ObjectList::append_menu_items_convert_unit(wxMenu* menu) [](wxCommandEvent&) { wxGetApp().plater()->convert_unit(false); }, "", menu); } +void ObjectList::append_menu_item_merge_to_multipart_object(wxMenu* menu) +{ + menu->AppendSeparator(); + append_menu_item(menu, wxID_ANY, _L("Merge"), _L("Merge objects to the one multipart object"), + [this](wxCommandEvent&) { merge(true); }, "", menu, + [this]() { return this->can_merge_to_multipart_object(); }, wxGetApp().plater()); +} + +void ObjectList::append_menu_item_merge_to_single_object(wxMenu* menu) +{ + menu->AppendSeparator(); + append_menu_item(menu, wxID_ANY, _L("Merge"), _L("Merge objects to the one single object"), + [this](wxCommandEvent&) { merge(false); }, "", menu, + [this]() { return this->can_merge_to_single_object(); }, wxGetApp().plater()); +} + void ObjectList::create_object_popupmenu(wxMenu *menu) { #ifdef __WXOSX__ @@ -1795,6 +1811,10 @@ void ObjectList::create_object_popupmenu(wxMenu *menu) // Split object to parts append_menu_item_split(menu); +// menu->AppendSeparator(); + + // Merge multipart object to the single object +// append_menu_item_merge_to_single_object(menu); menu->AppendSeparator(); // Layers Editing for object @@ -2365,6 +2385,107 @@ void ObjectList::split() changed_object(obj_idx); } +void ObjectList::merge(bool to_multipart_object) +{ + // merge selected objects to the multipart object + if (to_multipart_object) + { + std::vector<int> obj_idxs; + wxDataViewItemArray sels; + GetSelections(sels); + assert(!sels.IsEmpty()); + + for (wxDataViewItem item : sels) { + const ItemType type = m_objects_model->GetItemType(item); + assert(type & (itObject/* | itInstance*/)); + obj_idxs.emplace_back(type & itObject ? m_objects_model->GetIdByItem(item) : + m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item))); + } + std::sort(obj_idxs.begin(), obj_idxs.end()); + obj_idxs.erase(std::unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end()); + + if (obj_idxs.size() <= 1) + return; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Merge")); + + Model* model = (*m_objects)[0]->get_model(); + ModelObject* new_object = model->add_object(); + new_object->name = _u8L("Merged"); + DynamicPrintConfig* new_config = &new_object->config; + + const Vec3d& main_offset = (*m_objects)[0]->instances[0]->get_offset(); + + for (int obj_idx : obj_idxs) + { + ModelObject* object = (*m_objects)[obj_idx]; + Vec3d offset = object->instances[0]->get_offset(); + + if (object->id() == (*m_objects)[0]->id()) + new_object->add_instance(*object->instances[0]); + auto new_opt_keys = new_config->keys(); + + const DynamicPrintConfig& from_config = object->config; + auto opt_keys = from_config.keys(); + + // merge settings + for (auto& opt_key : opt_keys) { + if (find(new_opt_keys.begin(), new_opt_keys.end(), opt_key) == new_opt_keys.end()) { + const ConfigOption* option = from_config.option(opt_key); + if (!option) { + // if current option doesn't exist in prints.get_edited_preset(), + // get it from default config values + option = DynamicPrintConfig::new_from_defaults_keys({ opt_key })->option(opt_key); + } + new_config->set_key_value(opt_key, option->clone()); + } + } + + // merge volumes + for (const ModelVolume* volume : object->volumes) { + ModelVolume* new_volume = new_object->add_volume(*volume); + Vec3d vol_offset = offset - main_offset + new_volume->get_offset(); + new_volume->set_offset(vol_offset); + } + // save extruder value if it was set + if (object->volumes.size() == 1 && find(opt_keys.begin(), opt_keys.end(), "extruder") != opt_keys.end()) { + ModelVolume* volume = new_object->volumes.back(); + const ConfigOption* option = from_config.option("extruder"); + if (option) + volume->config.set_key_value("extruder", option->clone()); + } + + // merge layers + for (const auto& range : object->layer_config_ranges) + new_object->layer_config_ranges.emplace(range); + } + // remove selected objects + remove(); + // Add new object(merged) to the object_list + add_object_to_list(m_objects->size() - 1); + } + // merge all parts to the one single object + // all part's settings will be lost + else + { + wxDataViewItem item = GetSelection(); + if (!item) + return; + const int obj_idx = m_objects_model->GetIdByItem(item); + if (obj_idx == -1) + return; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Merge all parts to the one single object")); + + ModelObject* model_object = (*m_objects)[obj_idx]; + model_object->merge(); + + m_objects_model->DeleteVolumeChildren(item); + + changed_object(obj_idx); + } +} + void ObjectList::layers_editing() { const Selection& selection = scene_selection(); @@ -2490,6 +2611,31 @@ bool ObjectList::can_split_instances() return selection.is_multiple_full_instance() || selection.is_single_full_instance(); } +bool ObjectList::can_merge_to_multipart_object() const +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.IsEmpty()) + return false; + + // should be selected just objects + for (wxDataViewItem item : sels) + if (!(m_objects_model->GetItemType(item) & (itObject/* | itInstance*/))) + return false; + + return true; +} + +bool ObjectList::can_merge_to_single_object() const +{ + int obj_idx = get_selected_obj_idx(); + if (obj_idx < 0) + return false; + + // selected object should be multipart + return (*m_objects)[obj_idx]->volumes.size() > 1; +} + // NO_PARAMETERS function call means that changed object index will be determine from Selection() void ObjectList::changed_object(const int obj_idx/* = -1*/) const { @@ -4111,6 +4257,7 @@ void ObjectList::show_multi_selection_menu() }, wxGetApp().plater()); append_menu_items_convert_unit(menu); + append_menu_item_merge_to_multipart_object(menu); wxGetApp().plater()->PopupMenu(menu); } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 0924d4216..4490ec5e9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -254,6 +254,8 @@ public: void append_menu_item_delete(wxMenu* menu); void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu); void append_menu_items_convert_unit(wxMenu* menu); + 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); void create_sla_object_popupmenu(wxMenu*menu); void create_part_popupmenu(wxMenu*menu); @@ -277,6 +279,7 @@ public: void del_layers_from_object(const int obj_idx); bool del_subobject_from_object(const int obj_idx, const int idx, const int type); void split(); + void merge(bool to_multipart_object); void layers_editing(); wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item); @@ -287,6 +290,8 @@ public: bool is_splittable(); bool selected_instances_of_same_object(); bool can_split_instances(); + bool can_merge_to_multipart_object() const; + bool can_merge_to_single_object() const; wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); } wxBoxSizer* get_sizer() {return m_sizer;} diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a9bfcfbac..7f60b38b4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2297,9 +2297,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ if (imperial_units) convert_from_imperial_units(model); else if (model.looks_like_imperial_units()) { - wxMessageDialog msg_dlg(q, _L( - "This model looks like saved in inches.\n" - "Should I consider this model as a saved in inches and convert it?") + "\n", + wxMessageDialog msg_dlg(q, format_wxstr(_L( + "Some object(s) in file %s looks like saved in inches.\n" + "Should I consider them as a saved in inches and convert them?"), from_path(filename)) + "\n", _L("Saved in inches object detected"), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) convert_from_imperial_units(model);