diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 63686c294..e402baac5 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -652,23 +652,10 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) return v; } -static void add_v_to_volumes(ModelVolumePtrs* volumes, ModelVolume* v) -{ - if (volumes->empty() || v->type() >= volumes->back()->type()) - volumes->push_back(v); - else { - for (int pos = volumes->size() - 1; pos >= 0; pos--) - if (v->type() >= (*volumes)[pos]->type()) { - volumes->insert(volumes->begin() + pos + 1, v); - break; - } - } -} - ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh, ModelVolumeType type /*= ModelVolumeType::MODEL_PART*/) { ModelVolume* v = new ModelVolume(this, std::move(mesh), type); - add_v_to_volumes(&(this->volumes), v); + this->volumes.push_back(v); v->center_geometry_after_creation(); this->invalidate_bounding_box(); return v; @@ -679,7 +666,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t ModelVolume* v = new ModelVolume(this, other); if (type != ModelVolumeType::INVALID && v->type() != type) v->set_type(type); - add_v_to_volumes(&(this->volumes), v); + this->volumes.push_back(v); // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. // v->center_geometry_after_creation(); // this->invalidate_bounding_box(); @@ -728,6 +715,20 @@ void ModelObject::clear_volumes() this->invalidate_bounding_box(); } +void ModelObject::sort_volumes(bool full_sort) +{ + // sort volumes inside the object to order "Model Part, Negative Volume, Modifier, Support Blocker and Support Enforcer. " + if (full_sort) + std::stable_sort(volumes.begin(), volumes.end(), [](ModelVolume* vl, ModelVolume* vr) { + return vl->type() < vr->type(); + }); + // sort have to controll "place" of the support blockers/enforcers. But one of the model parts have to be on the first place. + else + std::stable_sort(volumes.begin(), volumes.end(), [](ModelVolume* vl, ModelVolume* vr) { + return vl->type() > ModelVolumeType::PARAMETER_MODIFIER && vl->type() < vr->type(); + }); +} + ModelInstance* ModelObject::add_instance() { ModelInstance* i = new ModelInstance(this); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index d665c7fe3..0f64d2c98 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -277,6 +277,7 @@ public: ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh); void delete_volume(size_t idx); void clear_volumes(); + void sort_volumes(bool full_sort); bool is_multiparts() const { return volumes.size() > 1; } ModelInstance* add_instance(); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 462594f0f..a32f8bb43 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -401,7 +401,12 @@ void GLVolume::set_render_color() void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) { - if (model_volume->is_modifier()) { + if (model_volume->is_negative_volume()) { + color[0] = 0.2f; + color[1] = 0.2f; + color[2] = 0.2f; + } + else if (model_volume->is_modifier()) { color[0] = 0.2f; color[1] = 1.0f; color[2] = 0.2f; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 26a2f305b..cfb85ce31 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -738,23 +738,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol if (volumes.empty()) return; - const auto object_item = m_objects_model->GetItemById(obj_idx); - - wxDataViewItemArray items; - - for (const ModelVolume* volume : volumes) - { - const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(), - volume->get_mesh_errors_count()>0 , - volume->config.has("extruder") ? volume->config.extruder() : 0); - add_settings_item(vol_item, &volume->config.get()); - items.Add(vol_item); - } - - changed_object(obj_idx); - - if (items.size() > 1) - { + wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) { + return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); }); + if (items.size() > 1) { m_selection_mode = smVolume; m_last_selected_item = wxDataViewItem(nullptr); } @@ -1149,7 +1135,7 @@ bool ObjectList::can_drop(const wxDataViewItem& item) const if (dragged_item_v_type == item_v_type && dragged_item_v_type != ModelVolumeType::MODEL_PART) return true; - if (wxGetApp().app_config->get("order_volumes") == "1" || // we can't reorder volumes outside of types + if ((wxGetApp().app_config->get("order_volumes") == "1" && dragged_item_v_type != item_v_type) || // we can't reorder volumes outside of types item_v_type >= ModelVolumeType::SUPPORT_BLOCKER) // support blockers/enforcers can't change its place return false; @@ -1359,27 +1345,25 @@ void ObjectList::load_subobject(ModelVolumeType type) take_snapshot(_L("Load Part")); - std::vector> volumes_info; - load_part((*m_objects)[obj_idx], volumes_info, type); + std::vector volumes; + load_part((*m_objects)[obj_idx], volumes, type); + wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) { + return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); }); - changed_object(obj_idx); if (type == ModelVolumeType::MODEL_PART) // update printable state on canvas wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx); - wxDataViewItem sel_item; - for (const auto& volume : volumes_info ) - sel_item = m_objects_model->AddVolumeChild(item, volume.first, type, volume.second); - - if (sel_item) - select_item(sel_item); + if (items.size() > 1) { + m_selection_mode = smVolume; + m_last_selected_item = wxDataViewItem(nullptr); + } + select_items(items); selection_changed(); } -void ObjectList::load_part( ModelObject* model_object, - std::vector> &volumes_info, - ModelVolumeType type) +void ObjectList::load_part(ModelObject* model_object, std::vector& added_volumes, ModelVolumeType type) { wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); @@ -1416,11 +1400,10 @@ void ObjectList::load_part( ModelObject* model_object, volume->translate(delta); auto new_volume = model_object->add_volume(*volume, type); new_volume->name = boost::filesystem::path(input_file).filename().string(); - - volumes_info.push_back(std::make_pair(from_u8(new_volume->name), new_volume->get_mesh_errors_count()>0)); - // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + + added_volumes.push_back(new_volume); } } } @@ -1507,14 +1490,19 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - changed_object(obj_idx); + select_item([this, obj_idx, new_volume]() { + wxDataViewItem sel_item; + + wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [new_volume](const ModelVolume* volume) { return volume == new_volume; }); + if (!items.IsEmpty()) + sel_item = items.front(); + + return sel_item; + }); if (type == ModelVolumeType::MODEL_PART) // update printable state on canvas wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx); - const auto object_item = m_objects_model->GetTopParent(GetSelection()); - select_item([this, object_item, name, type, new_volume]() { return m_objects_model->AddVolumeChild(object_item, name, type, - new_volume->get_mesh_errors_count() > 0); }); selection_changed(); } @@ -1556,6 +1544,7 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name new_object->add_instance(); // each object should have at list one instance ModelVolume* new_volume = new_object->add_volume(mesh); + new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); new_volume->name = into_u8(name); // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -1784,7 +1773,7 @@ void ObjectList::split() for (const ModelVolume* volume : model_object->volumes) { const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name), - volume->is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART, + volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART, volume->get_mesh_errors_count()>0, volume->config.has("extruder") ? volume->config.extruder() : 0, false); @@ -1940,6 +1929,7 @@ void ObjectList::merge(bool to_multipart_object) Vec3d vol_offset = volume_offset_correction* new_volume->get_offset(); new_volume->set_offset(vol_offset); } + new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); // merge settings auto new_opt_keys = config.keys(); @@ -3456,12 +3446,12 @@ void ObjectList::change_part_type() if (!volume) return; + const int obj_idx = get_selected_obj_idx(); + if (obj_idx < 0) return; + const ModelVolumeType type = volume->type(); if (type == ModelVolumeType::MODEL_PART) { - const int obj_idx = get_selected_obj_idx(); - if (obj_idx < 0) return; - int model_part_cnt = 0; for (auto vol : (*m_objects)[obj_idx]->volumes) { if (vol->type() == ModelVolumeType::MODEL_PART) @@ -3480,27 +3470,12 @@ void ObjectList::change_part_type() if (new_type == type || new_type == ModelVolumeType::INVALID) return; - take_snapshot(_(L("Change Part Type"))); + take_snapshot(_L("Change Part Type")); volume->set_type(new_type); - wxDataViewItem item = GetSelection(); - if (m_objects_model->GetItemType(item) != itVolume && m_objects_model->GetItemType(m_objects_model->GetParent(item)) == itVolume) - item = m_objects_model->GetParent(item); - m_objects_model->SetVolumeType(item, new_type); - - changed_object(get_selected_obj_idx()); - - // Update settings showing, if we have it - //(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer) - const auto settings_item = m_objects_model->GetSettingsItem(item); - if (settings_item && - (new_type == ModelVolumeType::SUPPORT_ENFORCER || new_type == ModelVolumeType::SUPPORT_BLOCKER || new_type == ModelVolumeType::NEGATIVE_VOLUME)) { - m_objects_model->Delete(settings_item); - } - else if (!settings_item && - (new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) { - add_settings_item(item, &volume->config.get()); - } + wxDataViewItemArray sel = reorder_volumes_and_get_selection(obj_idx, [volume](const ModelVolume* vol) { return vol == volume; }); + if (!sel.IsEmpty()) + select_item(sel.front()); } void ObjectList::last_volume_is_deleted(const int obj_idx) @@ -3906,6 +3881,42 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const wxGetApp().plater()->update(); } +wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection/* = nullptr*/) +{ + wxDataViewItemArray items; + + ModelObject* object = (*m_objects)[obj_idx]; + object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); + + wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx); + m_objects_model->DeleteVolumeChildren(object_item); + + for (const ModelVolume* volume : object->volumes) { + wxDataViewItem vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name), + volume->type(), + volume->get_mesh_errors_count() > 0, + volume->config.has("extruder") ? volume->config.extruder() : 0, + false); + // add settings to the part, if it has those + add_settings_item(vol_item, &volume->config.get()); + + if (add_to_selection && add_to_selection(volume)) + items.Add(vol_item); + } + + changed_object(obj_idx); + return items; +} + +void ObjectList::apply_volumes_order() +{ + if (wxGetApp().app_config->get("order_volumes") != "1" || !m_objects) + return; + + for (int obj_idx = 0; m_objects->size(); obj_idx++) + reorder_volumes_and_get_selection(obj_idx); +} + void ObjectList::update_after_undo_redo() { m_prevent_canvas_selection_update = true; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b378b00af..43d61d5c7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -238,7 +238,7 @@ public: bool is_instance_or_object_selected(); void load_subobject(ModelVolumeType type); - void load_part(ModelObject* model_object, std::vector> &volumes_info, ModelVolumeType type); + void load_part(ModelObject* model_object, std::vector &added_volumes, ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); void load_shape_object(const std::string &type_name); void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true); @@ -371,6 +371,8 @@ public: void toggle_printable_state(); void set_extruder_for_selected_items(const int extruder) const ; + wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection = nullptr); + void apply_volumes_order(); private: #ifdef __WXOSX__ diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index cc24a2c23..f703ddcdb 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -368,25 +368,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent if (insert_position >= 0) insert_position++; } - size_t new_volume_id = root->m_volumes_cnt; - - // find insert_position in respect to the volume type - for (int pos = (insert_position < 0 ? (int)root->GetChildCount() : insert_position) - 1; pos >= 0; pos--) { - ObjectDataViewModelNode* node = root->GetNthChild(pos); - if (volume_type >= node->m_volume_type) { - insert_position = pos + 1; - new_volume_id = (size_t)(node->GetIdx()) + 1; - for (int new_pos = pos + 1; new_pos < (int)root->GetChildCount(); new_pos++) { - ObjectDataViewModelNode* new_node = root->GetNthChild(new_pos); - if (new_node->GetType() != itVolume) - break; - new_node->SetIdx(new_node->GetIdx() + 1); - } - break; - } - } - - const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, has_errors), extruder_str, new_volume_id); + const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt); insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); // if part with errors is added, but object wasn't marked, then mark it diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 16457b31d..c09dc512c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1362,6 +1362,7 @@ void Sidebar::update_ui_from_settings() p->plater->canvas3D()->update_gizmos_on_off_state(); p->plater->set_current_canvas_as_dirty(); p->plater->get_current_canvas3D()->request_extra_frame(); + p->object_list->apply_volumes_order(); } std::vector& Sidebar::combos_filament() @@ -2449,6 +2450,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode #endif /* AUTOPLACEMENT_ON_LOAD */ for (ModelObject *model_object : model_objects) { auto *object = model.add_object(*model_object); + object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); std::string object_name = object->name.empty() ? fs::path(object->input_file).filename().string() : object->name; obj_idxs.push_back(obj_count++); @@ -3307,6 +3309,7 @@ void Plater::priv::reload_from_disk() if (!sinking) #endif // ENABLE_ALLOW_NEGATIVE_Z old_model_object->ensure_on_bed(); + old_model_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); sla::reproject_points_and_holes(old_model_object); }