Merge remote-tracking branch 'PRIVATE/master' into ys_emboss_with_master
This commit is contained in:
commit
fbcdd4191b
65 changed files with 6783 additions and 2921 deletions
src/slic3r/GUI
|
@ -12,6 +12,8 @@
|
|||
#include "GalleryDialog.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
#include "Gizmos/GLGizmoCut.hpp"
|
||||
#include "Gizmos/GLGizmoScale.hpp"
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "Tab.hpp"
|
||||
|
@ -402,6 +404,13 @@ MeshErrorsInfo ObjectList::get_mesh_errors_info(const int obj_idx, const int vol
|
|||
if (obj_idx < 0)
|
||||
return { {}, {} }; // hide tooltip
|
||||
|
||||
const ModelObject* object = (*m_objects)[obj_idx];
|
||||
if (vol_idx != -1 && vol_idx >= int(object->volumes.size())) {
|
||||
if (sidebar_info)
|
||||
*sidebar_info = _L("Wrong volume index ");
|
||||
return { {}, {} }; // hide tooltip
|
||||
}
|
||||
|
||||
const TriangleMeshStats& stats = vol_idx == -1 ?
|
||||
(*m_objects)[obj_idx]->get_object_stl_stats() :
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
|
||||
|
@ -682,6 +691,8 @@ void ObjectList::selection_changed()
|
|||
|
||||
fix_multiselection_conflicts();
|
||||
|
||||
fix_cut_selection();
|
||||
|
||||
// update object selection on Plater
|
||||
if (!m_prevent_canvas_selection_update)
|
||||
update_selections_on_canvas();
|
||||
|
@ -1404,6 +1415,15 @@ bool ObjectList::is_instance_or_object_selected()
|
|||
return selection.is_single_full_instance() || selection.is_single_full_object();
|
||||
}
|
||||
|
||||
bool ObjectList::is_selected_object_cut()
|
||||
{
|
||||
const Selection& selection = scene_selection();
|
||||
int obj_idx = selection.get_object_idx();
|
||||
if (obj_idx < 0)
|
||||
return false;
|
||||
return object(obj_idx)->is_cut();
|
||||
}
|
||||
|
||||
void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false*/)
|
||||
{
|
||||
if (type == ModelVolumeType::INVALID && from_galery) {
|
||||
|
@ -1723,6 +1743,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
// update printable state on canvas
|
||||
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
|
||||
|
||||
if (model_object.is_cut())
|
||||
update_info_items(obj_idx);
|
||||
|
||||
selection_changed();
|
||||
}
|
||||
|
||||
|
@ -1831,22 +1854,22 @@ void ObjectList::load_mesh_object(
|
|||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
void ObjectList::del_object(const int obj_idx)
|
||||
bool ObjectList::del_object(const int obj_idx)
|
||||
{
|
||||
wxGetApp().plater()->delete_object_from_model(obj_idx);
|
||||
return wxGetApp().plater()->delete_object_from_model(obj_idx);
|
||||
}
|
||||
|
||||
// Delete subobject
|
||||
void ObjectList::del_subobject_item(wxDataViewItem& item)
|
||||
bool ObjectList::del_subobject_item(wxDataViewItem& item)
|
||||
{
|
||||
if (!item) return;
|
||||
if (!item) return false;
|
||||
|
||||
int obj_idx, idx;
|
||||
ItemType type;
|
||||
|
||||
m_objects_model->GetItemInfo(item, type, obj_idx, idx);
|
||||
if (type == itUndef)
|
||||
return;
|
||||
return false;
|
||||
|
||||
wxDataViewItem parent = m_objects_model->GetParent(item);
|
||||
|
||||
|
@ -1860,19 +1883,21 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
|
|||
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
|
||||
else if (type & itInfo && obj_idx != -1)
|
||||
del_info_item(obj_idx, m_objects_model->GetInfoItemType(item));
|
||||
else if (idx == -1)
|
||||
return;
|
||||
else if (!del_subobject_from_object(obj_idx, idx, type))
|
||||
return;
|
||||
else if (idx == -1 || !del_subobject_from_object(obj_idx, idx, type))
|
||||
return false;
|
||||
|
||||
// If last volume item with warning was deleted, unmark object item
|
||||
if (type & itVolume) {
|
||||
add_volumes_to_object_in_list(obj_idx);
|
||||
const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats());
|
||||
m_objects_model->UpdateWarningIcon(parent, icon_name);
|
||||
}
|
||||
else
|
||||
m_objects_model->Delete(item);
|
||||
|
||||
m_objects_model->Delete(item);
|
||||
update_info_items(obj_idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
|
||||
|
@ -1895,6 +1920,10 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
|
|||
mv->seam_facets.reset();
|
||||
break;
|
||||
|
||||
case InfoItemType::CutConnectors:
|
||||
show_error(nullptr, _L("Connectors cannot be deleted from cut object."));
|
||||
break;
|
||||
|
||||
case InfoItemType::MmuSegmentation:
|
||||
cnv->get_gizmos_manager().reset_all_states();
|
||||
Plater::TakeSnapshot(plater, _L("Remove Multi Material painting"));
|
||||
|
@ -2005,6 +2034,16 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
|
|||
Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object."));
|
||||
return false;
|
||||
}
|
||||
if (object->is_cut()) {
|
||||
if (volume->is_model_part()) {
|
||||
Slic3r::GUI::show_error(nullptr, _L("Solid part cannot be deleted from cut object."));
|
||||
return false;
|
||||
}
|
||||
if (volume->is_negative_volume()) {
|
||||
Slic3r::GUI::show_error(nullptr, _L("Negative volume cannot be deleted from cut object."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
take_snapshot(_L("Delete Subobject"));
|
||||
|
||||
|
@ -2032,6 +2071,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
|
|||
Slic3r::GUI::show_error(nullptr, _L("Last instance of an object cannot be deleted."));
|
||||
return false;
|
||||
}
|
||||
if (object->is_cut()) {
|
||||
Slic3r::GUI::show_error(nullptr, _L("Instance cannot be deleted from cut object."));
|
||||
return false;
|
||||
}
|
||||
|
||||
take_snapshot(_L("Delete Instance"));
|
||||
object->delete_instance(idx);
|
||||
|
@ -2065,31 +2108,11 @@ void ObjectList::split()
|
|||
|
||||
volume->split(nozzle_dmrs_cnt);
|
||||
|
||||
(*m_objects)[obj_idx]->input_file.clear();
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
auto model_object = (*m_objects)[obj_idx];
|
||||
|
||||
auto parent = m_objects_model->GetTopParent(item);
|
||||
if (parent)
|
||||
m_objects_model->DeleteVolumeChildren(parent);
|
||||
else
|
||||
parent = item;
|
||||
|
||||
for (const ModelVolume* volume : model_object->volumes) {
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
|
||||
volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
|
||||
volume->text_configuration.has_value(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
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());
|
||||
}
|
||||
|
||||
model_object->input_file.clear();
|
||||
|
||||
if (parent == item)
|
||||
Expand(parent);
|
||||
add_volumes_to_object_in_list(obj_idx);
|
||||
|
||||
changed_object(obj_idx);
|
||||
// update printable state for new volumes on canvas3D
|
||||
|
@ -2408,9 +2431,12 @@ bool ObjectList::is_splittable(bool to_objects)
|
|||
auto obj_idx = get_selected_obj_idx();
|
||||
if (obj_idx < 0)
|
||||
return false;
|
||||
if ((*m_objects)[obj_idx]->volumes.size() > 1)
|
||||
const ModelObject* object = (*m_objects)[obj_idx];
|
||||
if (object->is_cut())
|
||||
return false;
|
||||
if (object->volumes.size() > 1)
|
||||
return true;
|
||||
return (*m_objects)[obj_idx]->volumes[0]->is_splittable();
|
||||
return object->volumes[0]->is_splittable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -2443,9 +2469,59 @@ bool ObjectList::can_split_instances()
|
|||
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
|
||||
}
|
||||
|
||||
bool ObjectList::has_selected_cut_object() const
|
||||
{
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (sels.IsEmpty())
|
||||
return false;
|
||||
|
||||
for (wxDataViewItem item : sels) {
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
if (obj_idx >= 0 && object(obj_idx)->is_cut())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void ObjectList::invalidate_cut_info_for_selection()
|
||||
{
|
||||
const wxDataViewItem item = GetSelection();
|
||||
if (item) {
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
if (obj_idx >= 0)
|
||||
invalidate_cut_info_for_object(size_t(obj_idx));
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::invalidate_cut_info_for_object(size_t obj_idx)
|
||||
{
|
||||
ModelObject* init_obj = object(int(obj_idx));
|
||||
if (!init_obj->is_cut())
|
||||
return;
|
||||
|
||||
take_snapshot(_L("Invalidate cut info"));
|
||||
|
||||
auto invalidate_cut = [this](size_t obj_idx) {
|
||||
object(int(obj_idx))->invalidate_cut();
|
||||
update_info_items(obj_idx);
|
||||
add_volumes_to_object_in_list(obj_idx);
|
||||
};
|
||||
|
||||
// invalidate cut for related objects (which have the same cut_id)
|
||||
for (size_t idx = 0; idx < m_objects->size(); idx++)
|
||||
if (ModelObject* obj = object(idx); obj != init_obj && obj->cut_id.is_equal(init_obj->cut_id))
|
||||
invalidate_cut(idx);
|
||||
|
||||
// invalidate own cut information
|
||||
invalidate_cut(size_t(obj_idx));
|
||||
|
||||
update_lock_icons_for_model();
|
||||
}
|
||||
|
||||
bool ObjectList::can_merge_to_multipart_object() const
|
||||
{
|
||||
if (printer_technology() == ptSLA)
|
||||
if (printer_technology() == ptSLA || has_selected_cut_object())
|
||||
return false;
|
||||
|
||||
wxDataViewItemArray sels;
|
||||
|
@ -2473,17 +2549,7 @@ bool ObjectList::can_merge_to_single_object() const
|
|||
|
||||
wxPoint ObjectList::get_mouse_position_in_control() const
|
||||
{
|
||||
wxPoint pt = wxGetMousePosition() - this->GetScreenPosition();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Workaround for OSX. From wxWidgets 3.1.6 Hittest doesn't respect to the header of wxDataViewCtrl
|
||||
if (wxDataViewItem top_item = this->GetTopItem(); top_item.IsOk()) {
|
||||
auto rect = this->GetItemRect(top_item, this->GetColumn(0));
|
||||
pt.y -= rect.y;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
return pt;
|
||||
return wxGetMousePosition() - this->GetScreenPosition();
|
||||
}
|
||||
|
||||
// NO_PARAMETERS function call means that changed object index will be determine from Selection()
|
||||
|
@ -2504,14 +2570,65 @@ void ObjectList::part_selection_changed()
|
|||
bool update_and_show_settings = false;
|
||||
bool update_and_show_layers = false;
|
||||
|
||||
bool enable_manipulation {true};
|
||||
bool disable_ss_manipulation {false};
|
||||
bool disable_ununiform_scale {false};
|
||||
|
||||
const auto item = GetSelection();
|
||||
|
||||
if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
|
||||
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
|
||||
|
||||
if (item && m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) {
|
||||
og_name = _L("Cut Connectors information");
|
||||
|
||||
update_and_show_manipulations = true;
|
||||
enable_manipulation = false;
|
||||
disable_ununiform_scale = true;
|
||||
}
|
||||
else if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
|
||||
og_name = _L("Group manipulation");
|
||||
|
||||
const Selection& selection = scene_selection();
|
||||
// don't show manipulation panel for case of all Object's parts selection
|
||||
update_and_show_manipulations = !selection.is_single_full_instance();
|
||||
|
||||
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) {
|
||||
if (selection.is_any_volume() || selection.is_any_modifier())
|
||||
enable_manipulation = !(*m_objects)[obj_idx]->is_cut();
|
||||
else// if (item && m_objects_model->GetItemType(item) == itInstanceRoot)
|
||||
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
|
||||
}
|
||||
else {
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (selection.is_single_full_object() || selection.is_multiple_full_instance() ) {
|
||||
int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
|
||||
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
|
||||
}
|
||||
else if (selection.is_mixed() || selection.is_multiple_full_object()) {
|
||||
std::map<CutObjectBase, std::set<int>> cut_objects;
|
||||
|
||||
// find cut objects
|
||||
for (auto item : sels) {
|
||||
int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
const ModelObject* obj = object(obj_idx);
|
||||
if (obj->is_cut()) {
|
||||
if (cut_objects.find(obj->cut_id) == cut_objects.end())
|
||||
cut_objects[obj->cut_id] = std::set<int>{ obj_idx };
|
||||
else
|
||||
cut_objects.at(obj->cut_id).insert(obj_idx);
|
||||
}
|
||||
}
|
||||
|
||||
// check if selected cut objects are "full selected"
|
||||
for (auto cut_object : cut_objects)
|
||||
if (cut_object.first.check_sum() != cut_object.second.size()) {
|
||||
disable_ss_manipulation = true;
|
||||
break;
|
||||
}
|
||||
disable_ununiform_scale = !cut_objects.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (item) {
|
||||
|
@ -2519,11 +2636,12 @@ void ObjectList::part_selection_changed()
|
|||
const wxDataViewItem parent = m_objects_model->GetParent(item);
|
||||
const ItemType parent_type = m_objects_model->GetItemType(parent);
|
||||
obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
ModelObject* object = (*m_objects)[obj_idx];
|
||||
|
||||
if (parent == wxDataViewItem(nullptr)
|
||||
|| type == itInfo) {
|
||||
og_name = _L("Object manipulation");
|
||||
m_config = &(*m_objects)[obj_idx]->config;
|
||||
m_config = &object->config;
|
||||
update_and_show_manipulations = true;
|
||||
|
||||
if (type == itInfo) {
|
||||
|
@ -2539,29 +2657,30 @@ void ObjectList::part_selection_changed()
|
|||
case InfoItemType::CustomSeam:
|
||||
case InfoItemType::MmuSegmentation:
|
||||
{
|
||||
GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports :
|
||||
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
|
||||
GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports :
|
||||
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
|
||||
GLGizmosManager::EType::MmuSegmentation;
|
||||
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
|
||||
if (gizmos_mgr.get_current_type() != gizmo_type)
|
||||
gizmos_mgr.open_gizmo(gizmo_type);
|
||||
break;
|
||||
}
|
||||
case InfoItemType::Sinking: { break; }
|
||||
case InfoItemType::Sinking:
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
else
|
||||
disable_ss_manipulation = object->is_cut();
|
||||
}
|
||||
else {
|
||||
if (type & itSettings) {
|
||||
if (parent_type & itObject) {
|
||||
og_name = _L("Object Settings to modify");
|
||||
m_config = &(*m_objects)[obj_idx]->config;
|
||||
m_config = &object->config;
|
||||
}
|
||||
else if (parent_type & itVolume) {
|
||||
og_name = _L("Part Settings to modify");
|
||||
volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||
m_config = &object->volumes[volume_id]->config;
|
||||
}
|
||||
else if (parent_type & itLayer) {
|
||||
og_name = _L("Layer range Settings to modify");
|
||||
|
@ -2572,15 +2691,18 @@ void ObjectList::part_selection_changed()
|
|||
else if (type & itVolume) {
|
||||
og_name = _L("Part manipulation");
|
||||
volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||
m_config = &object->volumes[volume_id]->config;
|
||||
update_and_show_manipulations = true;
|
||||
const ModelVolume* volume = object->volumes[volume_id];
|
||||
enable_manipulation = !(object->is_cut() && (volume->is_cut_connector() || volume->is_model_part()));
|
||||
}
|
||||
else if (type & itInstance) {
|
||||
og_name = _L("Instance manipulation");
|
||||
update_and_show_manipulations = true;
|
||||
|
||||
// fill m_config by object's values
|
||||
m_config = &(*m_objects)[obj_idx]->config;
|
||||
m_config = &object->config;
|
||||
disable_ss_manipulation = object->is_cut();
|
||||
}
|
||||
else if (type & (itLayerRoot|itLayer)) {
|
||||
og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range");
|
||||
|
@ -2599,10 +2721,20 @@ void ObjectList::part_selection_changed()
|
|||
wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " ");
|
||||
|
||||
if (item) {
|
||||
// wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item));
|
||||
wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item));
|
||||
wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_info(obj_idx, volume_id));
|
||||
}
|
||||
|
||||
if (disable_ss_manipulation)
|
||||
wxGetApp().obj_manipul()->DisableScale();
|
||||
else {
|
||||
wxGetApp().obj_manipul()->Enable(enable_manipulation);
|
||||
if (disable_ununiform_scale)
|
||||
wxGetApp().obj_manipul()->DisableUnuniformScale();
|
||||
}
|
||||
|
||||
if (GLGizmoScale3D* scale = dynamic_cast<GLGizmoScale3D*>(gizmos_mgr.get_gizmo(GLGizmosManager::Scale)))
|
||||
scale->enable_ununiversal_scale(!disable_ununiform_scale);
|
||||
}
|
||||
|
||||
if (update_and_show_settings)
|
||||
|
@ -2625,6 +2757,7 @@ void ObjectList::part_selection_changed()
|
|||
#else
|
||||
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
wxGetApp().plater()->canvas3D()->enable_moving(enable_manipulation); // ysFIXME
|
||||
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
|
||||
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
|
||||
wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers);
|
||||
|
@ -2683,6 +2816,7 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
|
|||
|
||||
for (InfoItemType type : {InfoItemType::CustomSupports,
|
||||
InfoItemType::CustomSeam,
|
||||
InfoItemType::CutConnectors,
|
||||
InfoItemType::MmuSegmentation,
|
||||
InfoItemType::Sinking,
|
||||
InfoItemType::VariableLayerHeight}) {
|
||||
|
@ -2703,6 +2837,9 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
|
|||
});
|
||||
break;
|
||||
|
||||
case InfoItemType::CutConnectors:
|
||||
should_show = model_object->is_cut() && model_object->has_connectors() && model_object->volumes.size() > 1;
|
||||
break;
|
||||
case InfoItemType::VariableLayerHeight :
|
||||
should_show = printer_technology() == ptFFF
|
||||
&& ! model_object->layer_height_profile.empty();
|
||||
|
@ -2742,33 +2879,74 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
|
|||
}
|
||||
}
|
||||
|
||||
static wxString extruder2str(int extruder)
|
||||
{
|
||||
return extruder == 0 ? _L("default") : wxString::Format("%d", extruder);
|
||||
}
|
||||
|
||||
static bool can_add_volumes_to_object(const ModelObject* object)
|
||||
{
|
||||
bool can = object->volumes.size() > 1;
|
||||
|
||||
if (can && object->is_cut()) {
|
||||
int no_connectors_cnt = 0;
|
||||
for (const ModelVolume* v : object->volumes)
|
||||
if (!v->is_cut_connector()) {
|
||||
if (!v->is_model_part())
|
||||
return true;
|
||||
no_connectors_cnt++;
|
||||
}
|
||||
can = no_connectors_cnt > 1;
|
||||
}
|
||||
|
||||
return can;
|
||||
}
|
||||
|
||||
wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
|
||||
{
|
||||
wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx));
|
||||
m_objects_model->DeleteVolumeChildren(object_item);
|
||||
|
||||
wxDataViewItemArray items;
|
||||
|
||||
const ModelObject* object = (*m_objects)[obj_idx];
|
||||
// add volumes to the object
|
||||
if (can_add_volumes_to_object(object)) {
|
||||
int volume_idx{ -1 };
|
||||
for (const ModelVolume* volume : object->volumes) {
|
||||
++volume_idx;
|
||||
if (object->is_cut() && volume->is_cut_connector())
|
||||
continue;
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item,
|
||||
from_u8(volume->name),
|
||||
volume_idx,
|
||||
volume->type(),
|
||||
volume->text_configuration.has_value(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
extruder2str(volume->config.has("extruder") ? volume->config.extruder() : 0));
|
||||
add_settings_item(vol_item, &volume->config.get());
|
||||
|
||||
if (add_to_selection && add_to_selection(volume))
|
||||
items.Add(vol_item);
|
||||
}
|
||||
Expand(object_item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
|
||||
{
|
||||
auto model_object = (*m_objects)[obj_idx];
|
||||
const wxString& item_name = from_u8(model_object->name);
|
||||
const auto item = m_objects_model->Add(item_name,
|
||||
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
|
||||
get_warning_icon_name(model_object->mesh().stats()));
|
||||
const auto item = m_objects_model->AddObject(item_name,
|
||||
extruder2str(model_object->config.has("extruder") ? model_object->config.extruder() : 0),
|
||||
get_warning_icon_name(model_object->mesh().stats()),
|
||||
model_object->is_cut());
|
||||
|
||||
update_info_items(obj_idx, nullptr, call_selection_changed);
|
||||
|
||||
// add volumes to the object
|
||||
if (model_object->volumes.size() > 1 ||
|
||||
model_object->volumes[0]->text_configuration.has_value()) {
|
||||
for (const ModelVolume* volume : model_object->volumes) {
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item,
|
||||
from_u8(volume->name),
|
||||
volume->type(),
|
||||
volume->text_configuration.has_value(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
add_settings_item(vol_item, &volume->config.get());
|
||||
}
|
||||
Expand(item);
|
||||
}
|
||||
add_volumes_to_object_in_list(obj_idx);
|
||||
|
||||
// add instances to the object, if it has those
|
||||
if (model_object->instances.size()>1)
|
||||
|
@ -2822,29 +3000,39 @@ void ObjectList::delete_instance_from_list(const size_t obj_idx, const size_t in
|
|||
select_item([this, obj_idx, inst_idx]() { return m_objects_model->Delete(m_objects_model->GetItemByInstanceId(obj_idx, inst_idx)); });
|
||||
}
|
||||
|
||||
void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx)
|
||||
void ObjectList::update_lock_icons_for_model()
|
||||
{
|
||||
if ( !(type&(itObject|itVolume|itInstance)) )
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Delete Selected Item")));
|
||||
|
||||
if (type&itObject) {
|
||||
del_object(obj_idx);
|
||||
delete_object_from_list(obj_idx);
|
||||
}
|
||||
else {
|
||||
del_subobject_from_object(obj_idx, sub_obj_idx, type);
|
||||
|
||||
type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) :
|
||||
delete_instance_from_list(obj_idx, sub_obj_idx);
|
||||
}
|
||||
for (size_t obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx)
|
||||
if (!(*m_objects)[obj_idx]->is_cut())
|
||||
m_objects_model->UpdateLockIcon(m_objects_model->GetItemById(int(obj_idx)), false);
|
||||
}
|
||||
|
||||
void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete)
|
||||
bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx)
|
||||
{
|
||||
if (type & (itObject | itVolume | itInstance)) {
|
||||
if (type & itObject) {
|
||||
bool was_cut = object(obj_idx)->is_cut();
|
||||
if (del_object(obj_idx)) {
|
||||
delete_object_from_list(obj_idx);
|
||||
if (was_cut)
|
||||
update_lock_icons_for_model();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) {
|
||||
type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) :
|
||||
delete_instance_from_list(obj_idx, sub_obj_idx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete)
|
||||
{
|
||||
if (items_for_delete.empty())
|
||||
return;
|
||||
return false;
|
||||
|
||||
m_prevent_list_events = true;
|
||||
|
||||
|
@ -2853,14 +3041,18 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
|
|||
if (!(item->type&(itObject | itVolume | itInstance)))
|
||||
continue;
|
||||
if (item->type&itObject) {
|
||||
del_object(item->obj_idx);
|
||||
bool was_cut = object(item->obj_idx)->is_cut();
|
||||
if (!del_object(item->obj_idx))
|
||||
return false;// continue;
|
||||
m_objects_model->Delete(m_objects_model->GetItemById(item->obj_idx));
|
||||
if (was_cut)
|
||||
update_lock_icons_for_model();
|
||||
}
|
||||
else {
|
||||
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
|
||||
continue;
|
||||
if (item->type&itVolume) {
|
||||
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
|
||||
add_volumes_to_object_in_list(item->obj_idx);
|
||||
ModelObject* obj = object(item->obj_idx);
|
||||
if (obj->volumes.size() == 1) {
|
||||
wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx);
|
||||
|
@ -2884,8 +3076,12 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
|
|||
update_info_items(id);
|
||||
}
|
||||
|
||||
m_prevent_list_events = true;
|
||||
m_prevent_list_events = false;
|
||||
if (modified_objects_ids.empty())
|
||||
return false;
|
||||
part_selection_changed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectList::delete_all_objects_from_list()
|
||||
|
@ -2990,8 +3186,10 @@ void ObjectList::remove()
|
|||
{
|
||||
wxDataViewItem parent = m_objects_model->GetParent(item);
|
||||
ItemType type = m_objects_model->GetItemType(item);
|
||||
if (type & itObject)
|
||||
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
|
||||
if (type & itObject) {
|
||||
if (!delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1))
|
||||
return item;
|
||||
}
|
||||
else {
|
||||
if (type & (itLayer | itInstance)) {
|
||||
// In case there is just one layer or two instances and we delete it, del_subobject_item will
|
||||
|
@ -3001,7 +3199,8 @@ void ObjectList::remove()
|
|||
parent = m_objects_model->GetTopParent(item);
|
||||
}
|
||||
|
||||
del_subobject_item(item);
|
||||
if (!del_subobject_item(item))
|
||||
return item;
|
||||
}
|
||||
|
||||
return parent;
|
||||
|
@ -3027,6 +3226,8 @@ void ObjectList::remove()
|
|||
if (m_objects_model->InvalidItem(item)) // item can be deleted for this moment (like last 2 Instances or Volumes)
|
||||
continue;
|
||||
parent = delete_item(item);
|
||||
if (parent == item && m_objects_model->GetItemType(item) & itObject) // Object wasn't deleted
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3219,7 +3420,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range,
|
|||
|
||||
const auto layer_item = m_objects_model->AddLayersChild(layers_item,
|
||||
range,
|
||||
config.opt_int("extruder"),
|
||||
extruder2str(config.opt_int("extruder")),
|
||||
layer_idx);
|
||||
add_settings_item(layer_item, &config);
|
||||
}
|
||||
|
@ -3313,6 +3514,24 @@ bool ObjectList::is_selected(const ItemType type) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ObjectList::is_connectors_item_selected() const
|
||||
{
|
||||
const wxDataViewItem& item = GetSelection();
|
||||
if (item)
|
||||
return m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectList::is_connectors_item_selected(const wxDataViewItemArray& sels) const
|
||||
{
|
||||
for (auto item : sels)
|
||||
if (m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ObjectList::get_selected_layers_range_idx() const
|
||||
{
|
||||
const wxDataViewItem& item = GetSelection();
|
||||
|
@ -3439,11 +3658,18 @@ void ObjectList::update_selections()
|
|||
else {
|
||||
for (auto idx : selection.get_volume_idxs()) {
|
||||
const auto gl_vol = selection.get_volume(idx);
|
||||
if (gl_vol->volume_idx() >= 0)
|
||||
if (gl_vol->volume_idx() >= 0) {
|
||||
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
|
||||
// are not associated with ModelVolumes, but they are temporarily generated by the backend
|
||||
// (for example, SLA supports or SLA pad).
|
||||
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
|
||||
int obj_idx = gl_vol->object_idx();
|
||||
int vol_idx = gl_vol->volume_idx();
|
||||
assert(obj_idx >= 0 && vol_idx >= 0);
|
||||
if (object(obj_idx)->volumes[vol_idx]->is_cut_connector())
|
||||
sels.Add(m_objects_model->GetInfoItemByType(m_objects_model->GetItemById(obj_idx), InfoItemType::CutConnectors));
|
||||
else
|
||||
sels.Add(m_objects_model->GetItemByVolumeId(obj_idx, vol_idx));
|
||||
}
|
||||
}
|
||||
m_selection_mode = smVolume; }
|
||||
}
|
||||
|
@ -3493,11 +3719,34 @@ void ObjectList::update_selections()
|
|||
|
||||
if (sels.size() == 0 || m_selection_mode & smSettings)
|
||||
m_selection_mode = smUndef;
|
||||
|
||||
select_items(sels);
|
||||
|
||||
// Scroll selected Item in the middle of an object list
|
||||
ensure_current_item_visible();
|
||||
if (fix_cut_selection(sels) || is_connectors_item_selected(sels)) {
|
||||
m_prevent_list_events = true;
|
||||
|
||||
// If some part is selected, unselect all items except of selected parts of the current object
|
||||
UnselectAll();
|
||||
SetSelections(sels);
|
||||
|
||||
m_prevent_list_events = false;
|
||||
|
||||
// update object selection on Plater
|
||||
if (!m_prevent_canvas_selection_update)
|
||||
update_selections_on_canvas();
|
||||
|
||||
// to update the toolbar and info sizer
|
||||
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject || is_connectors_item_selected()) {
|
||||
auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT);
|
||||
event.SetEventObject(this);
|
||||
wxPostEvent(this, event);
|
||||
}
|
||||
part_selection_changed();
|
||||
}
|
||||
else {
|
||||
select_items(sels);
|
||||
|
||||
// Scroll selected Item in the middle of an object list
|
||||
ensure_current_item_visible();
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::update_selections_on_canvas()
|
||||
|
@ -3531,16 +3780,29 @@ void ObjectList::update_selections_on_canvas()
|
|||
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
|
||||
}
|
||||
else if (type == itInfo) {
|
||||
// When selecting an info item, select one instance of the
|
||||
// respective object - a gizmo may want to be opened.
|
||||
int inst_idx = selection.get_instance_idx();
|
||||
int scene_obj_idx = selection.get_object_idx();
|
||||
mode = Selection::Instance;
|
||||
// select first instance, unless an instance of the object is already selected
|
||||
if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx)
|
||||
inst_idx = 0;
|
||||
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx);
|
||||
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
|
||||
if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) {
|
||||
mode = Selection::Volume;
|
||||
|
||||
// When selecting CutConnectors info item, select all object volumes, which are marked as a connector
|
||||
const ModelObject* obj = object(obj_idx);
|
||||
for (unsigned int vol_idx = 0; vol_idx < obj->volumes.size(); vol_idx++)
|
||||
if (obj->volumes[vol_idx]->is_cut_connector()) {
|
||||
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_volume(obj_idx, std::max(instance_idx, 0), vol_idx);
|
||||
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// When selecting an info item, select one instance of the
|
||||
// respective object - a gizmo may want to be opened.
|
||||
int inst_idx = selection.get_instance_idx();
|
||||
int scene_obj_idx = selection.get_object_idx();
|
||||
mode = Selection::Instance;
|
||||
// select first instance, unless an instance of the object is already selected
|
||||
if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx)
|
||||
inst_idx = 0;
|
||||
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx);
|
||||
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3556,6 +3818,8 @@ void ObjectList::update_selections_on_canvas()
|
|||
|
||||
if (sel_cnt == 1) {
|
||||
wxDataViewItem item = GetSelection();
|
||||
if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors)
|
||||
selection.remove_all();
|
||||
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
|
||||
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode);
|
||||
else
|
||||
|
@ -3826,6 +4090,53 @@ void ObjectList::fix_multiselection_conflicts()
|
|||
m_prevent_list_events = false;
|
||||
}
|
||||
|
||||
bool ObjectList::fix_cut_selection(wxDataViewItemArray& sels)
|
||||
{
|
||||
if (wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Scale) {
|
||||
for (const auto& item : sels) {
|
||||
if (m_objects_model->GetItemType(item) & (itInstance | itObject) ||
|
||||
(m_objects_model->GetItemType(item) & itSettings &&
|
||||
m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject)) {
|
||||
|
||||
bool is_instance_selection = m_objects_model->GetItemType(item) & itInstance;
|
||||
|
||||
int object_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
int inst_idx = is_instance_selection ? m_objects_model->GetInstanceIdByItem(item) : 0;
|
||||
|
||||
if (auto obj = object(object_idx); obj->is_cut()) {
|
||||
sels.Clear();
|
||||
|
||||
auto cut_id = obj->cut_id;
|
||||
|
||||
int objects_cnt = int((*m_objects).size());
|
||||
for (int obj_idx = 0; obj_idx < objects_cnt; ++obj_idx) {
|
||||
auto object = (*m_objects)[obj_idx];
|
||||
if (object->is_cut() && object->cut_id.has_same_id(cut_id))
|
||||
sels.Add(is_instance_selection ? m_objects_model->GetItemByInstanceId(obj_idx, inst_idx) : m_objects_model->GetItemById(obj_idx));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectList::fix_cut_selection()
|
||||
{
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (fix_cut_selection(sels)) {
|
||||
m_prevent_list_events = true;
|
||||
|
||||
// If some part is selected, unselect all items except of selected parts of the current object
|
||||
UnselectAll();
|
||||
SetSelections(sels);
|
||||
|
||||
m_prevent_list_events = false;
|
||||
}
|
||||
}
|
||||
|
||||
ModelVolume* ObjectList::get_selected_model_volume()
|
||||
{
|
||||
wxDataViewItem item = GetSelection();
|
||||
|
@ -3856,10 +4167,11 @@ void ObjectList::change_part_type()
|
|||
if (obj_idx < 0) return;
|
||||
|
||||
const ModelVolumeType type = volume->type();
|
||||
const ModelObject* obj = object(obj_idx);
|
||||
if (type == ModelVolumeType::MODEL_PART)
|
||||
{
|
||||
int model_part_cnt = 0;
|
||||
for (auto vol : (*m_objects)[obj_idx]->volumes) {
|
||||
for (auto vol : obj->volumes) {
|
||||
if (vol->type() == ModelVolumeType::MODEL_PART)
|
||||
++model_part_cnt;
|
||||
}
|
||||
|
@ -3870,14 +4182,19 @@ void ObjectList::change_part_type()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
wxArrayString names;
|
||||
for (const wxString& name : { _L("Part"), _L("Negative Volume"), _L("Modifier") })
|
||||
names.Add(name);
|
||||
// names.Alloc(is_cut_object ? 3 : 5);
|
||||
if (!is_cut_object)
|
||||
for (const wxString& type : { _L("Part"), _L("Negative Volume") })
|
||||
names.Add(type);
|
||||
names.Add(_L("Modifier"))
|
||||
if (!volume->text_configuration.has_value())
|
||||
for (const wxString& name : { _L("Support Blocker"), _L("Support Enforcer") })
|
||||
names.Add(name);
|
||||
auto new_type = ModelVolumeType(wxGetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type)));
|
||||
|
||||
const int type_shift = is_cut_object ? 2 : 0;
|
||||
auto new_type = ModelVolumeType(type_shift + wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type) - type_shift));
|
||||
if (new_type == type || new_type == ModelVolumeType::INVALID)
|
||||
return;
|
||||
|
||||
|
@ -4397,34 +4714,14 @@ 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<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
|
||||
wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
|
||||
{
|
||||
wxDataViewItemArray items;
|
||||
(*m_objects)[obj_idx]->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
||||
|
||||
ModelObject* object = (*m_objects)[obj_idx];
|
||||
if (object->volumes.size() <= 1)
|
||||
return items;
|
||||
wxDataViewItemArray items = add_volumes_to_object_in_list(obj_idx, std::move(add_to_selection));
|
||||
|
||||
object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
||||
changed_object(int(obj_idx));
|
||||
|
||||
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->text_configuration.has_value(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue