diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 77799f6fa..8bf6c3689 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -11,6 +11,7 @@ #include "../libslic3r.h" #include "../BoundingBox.hpp" #include "../PrintConfig.hpp" +#include "../Utils.hpp" namespace Slic3r { @@ -40,7 +41,7 @@ struct FillParams // in this case we don't try to make more continuous paths bool complete; }; -static_assert(std::is_trivially_copyable::value, "FillParams class is not POD (and it should be - see constructor)."); +static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); class Fill { diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 028605fd5..094527850 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -10,6 +10,7 @@ #include #include "libslic3r.h" +#include "Utils.hpp" namespace Slic3r { @@ -93,7 +94,7 @@ struct SlicingParameters coordf_t object_print_z_min; coordf_t object_print_z_max; }; -static_assert(std::is_trivially_copyable::value, "SlicingParameters class is not POD (and it should be - see constructor)."); +static_assert(IsTriviallyCopyable::value, "SlicingParameters class is not POD (and it should be - see constructor)."); // The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action. diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index cfae9edd1..ed12d0559 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -159,6 +159,16 @@ template size_t next_highest_power_of_2(T v, extern std::string xml_escape(std::string text); +#if defined __GNUC__ & __GNUC__ < 5 +// Older GCCs don't have std::is_trivially_copyable +// cf. https://gcc.gnu.org/onlinedocs/gcc-4.9.4/libstdc++/manual/manual/status.html#status.iso.2011 +#warning "GCC version < 5, faking std::is_trivially_copyable" +template struct IsTriviallyCopyable { static constexpr bool value = true; }; +#else +template struct IsTriviallyCopyable : public std::is_trivially_copyable {}; +#endif + + class ScopeGuard { public: diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 411895b03..42899b96b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -856,6 +856,14 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, return true; } +void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) +{ + m_config = config; + delete m_slicing_parameters; + m_slicing_parameters = nullptr; + m_layers_texture.valid = false; +} + void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) { const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr; @@ -864,6 +872,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) m_layer_height_profile_modified = false; delete m_slicing_parameters; m_slicing_parameters = nullptr; + m_layers_texture.valid = false; } this->last_object_id = object_id; m_model_object = model_object_new; @@ -1167,6 +1176,13 @@ void GLCanvas3D::LayersEditing::adjust_layer_height_profile() m_layers_texture.valid = false; } +void GLCanvas3D::LayersEditing::reset_layer_height_profile() +{ + const_cast(m_model_object)->layer_height_profile.clear(); + m_layer_height_profile.clear(); + m_layers_texture.valid = false; +} + void GLCanvas3D::LayersEditing::generate_layer_height_texture() { this->update_slicing_parameters(); @@ -5024,7 +5040,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftDown()) { // A volume is selected and the mouse is inside the reset button. Reset the ModelObject's layer height profile. - m_model->objects[layer_editing_object_idx]->layer_height_profile.clear(); + m_layers_editing.reset_layer_height_profile(); // Index 2 means no editing, just wait for mouse up event. m_layers_editing.state = LayersEditing::Completed; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 11ef055af..8a700cfeb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -346,7 +346,7 @@ class GLCanvas3D ~LayersEditing(); bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - void set_config(const DynamicPrintConfig* config) { m_config = config; delete m_slicing_parameters; m_slicing_parameters = nullptr; } + void set_config(const DynamicPrintConfig* config); void select_object(const Model &model, int object_id); bool is_allowed() const; @@ -358,9 +358,9 @@ class GLCanvas3D void render_overlay(const GLCanvas3D& canvas) const; void render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection& volumes) const; - void generate_layer_height_texture(); void adjust_layer_height_profile(); void accept_changes(GLCanvas3D& canvas); + void reset_layer_height_profile(); static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); @@ -374,6 +374,7 @@ class GLCanvas3D private: bool _is_initialized() const; + void generate_layer_height_texture(); void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const Rect& reset_rect) const; void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 38e1dfd9c..83f5951c2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -97,7 +97,7 @@ void ObjectList::create_objects_ctrl() // temporary workaround for the correct behavior of the Scrolled sidebar panel: // 1. set a height of the list to some big value // 2. change it to the normal min value (200) after first whole App updating/layouting - SetMinSize(wxSize(-1, 1500)); // #ys_FIXME + SetMinSize(wxSize(-1, 3000)); // #ys_FIXME m_sizer = new wxBoxSizer(wxVERTICAL); m_sizer->Add(this, 1, wxGROW | wxLEFT, 20); @@ -445,13 +445,21 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) // only allow drags for item, not containers if (multiple_selection() || GetSelection()!=item || - m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume ) { + m_objects_model->GetParent(item) == wxDataViewItem(0)) { + event.Veto(); + return; + } + + const ItemType& type = m_objects_model->GetItemType(item); + if (!(type & (itVolume | itInstance))) { event.Veto(); return; } - m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), m_objects_model->GetVolumeIdByItem(item)); + m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), + type&itVolume ? m_objects_model->GetVolumeIdByItem(item) : + m_objects_model->GetInstanceIdByItem(item), + type); /* Under MSW or OSX, DnD moves an item to the place of another selected item * But under GTK, DnD moves an item between another two items. @@ -470,31 +478,41 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move; } +bool ObjectList::can_drop(const wxDataViewItem& item) const +{ + return m_dragged_data.type() == itInstance && !item.IsOk() || + m_dragged_data.type() == itVolume && item.IsOk() && + m_objects_model->GetItemType(item) == itVolume && + m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item); +} + void ObjectList::OnDropPossible(wxDataViewEvent &event) { - wxDataViewItem item(event.GetItem()); + const wxDataViewItem& item = event.GetItem(); - // only allow drags for item or background, not containers - if (!item.IsOk() || - m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume || - m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) + if (!can_drop(item)) event.Veto(); } void ObjectList::OnDrop(wxDataViewEvent &event) { - wxDataViewItem item(event.GetItem()); + const wxDataViewItem& item = event.GetItem(); - if (!item.IsOk() || m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume || - m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) { + if (!can_drop(item)) + { event.Veto(); m_dragged_data.clear(); return; } - const int from_volume_id = m_dragged_data.vol_idx(); + if (m_dragged_data.type() == itInstance) + { + instance_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.sub_obj_idx()); + m_dragged_data.clear(); + return; + } + + const int from_volume_id = m_dragged_data.sub_obj_idx(); int to_volume_id = m_objects_model->GetVolumeIdByItem(item); // It looks like a fixed in current version of the wxWidgets @@ -506,7 +524,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event) // if (to_volume_id > from_volume_id) to_volume_id--; // #endif // __WXGTK__ - auto& volumes = (*m_objects)[/*m_selected_object_id*/m_dragged_data.obj_idx()]->volumes; + auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes; auto delta = to_volume_id < from_volume_id ? -1 : 1; int cnt = 0; for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) @@ -516,7 +534,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event) m_objects_model->GetParent(item))); m_parts_changed = true; - parts_changed(/*m_selected_object_id*/m_dragged_data.obj_idx()); + parts_changed(m_dragged_data.obj_idx()); m_dragged_data.clear(); } @@ -1733,6 +1751,25 @@ void ObjectList::update_settings_items() UnselectAll(); } +void ObjectList::instance_to_separated_object(const int obj_idx, const int inst_idx) +{ + // create new object from selected instance + ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]); + for (int i = model_object->instances.size() - 1; i >= 0; i--) + { + if (i == inst_idx) + continue; + model_object->delete_instance(i); + } + + // Add new object to the object_list + add_object_to_list(m_objects->size() - 1); + + // delete selected instance from the object + del_subobject_from_object(obj_idx, inst_idx, itInstance); + delete_instance_from_list(obj_idx, inst_idx); +} + void ObjectList::ItemValueChanged(wxDataViewEvent &event) { if (event.GetColumn() == 0) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 7631782df..21db30ad5 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -56,22 +56,27 @@ class ObjectList : public wxDataViewCtrl struct dragged_item_data { - void init(const int obj_idx, const int vol_idx) { + void init(const int obj_idx, const int subobj_idx, const ItemType type) { m_obj_idx = obj_idx; - m_vol_idx = vol_idx; + m_subobj_idx = subobj_idx; + m_type = type; } void clear() { m_obj_idx = -1; - m_vol_idx = -1; + m_subobj_idx = -1; + m_type = itUndef; } int obj_idx() const { return m_obj_idx; } - int vol_idx() const { return m_vol_idx; } + int sub_obj_idx() const { return m_subobj_idx; } + ItemType type() const { return m_type; } private: int m_obj_idx = -1; - int m_vol_idx = -1; + int m_subobj_idx = -1; + ItemType m_type = itUndef; + } m_dragged_data; wxBoxSizer *m_sizer {nullptr}; @@ -222,6 +227,8 @@ public: bool has_multi_part_objects(); void update_settings_items(); + void instance_to_separated_object(const int obj_idx, const int inst_idx); + private: void OnChar(wxKeyEvent& event); void OnContextMenu(wxDataViewEvent &event); @@ -229,6 +236,7 @@ private: void OnBeginDrag(wxDataViewEvent &event); void OnDropPossible(wxDataViewEvent &event); void OnDrop(wxDataViewEvent &event); + bool can_drop(const wxDataViewItem& item) const ; void ItemValueChanged(wxDataViewEvent &event); void OnEditingDone(wxDataViewEvent &event); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dcbea81dd..5fc4bef32 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1066,6 +1066,7 @@ private: bool can_delete_object() const; bool can_increase_instances() const; bool can_decrease_instances() const; + bool can_set_instance_to_object() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; bool can_split() const; @@ -2378,11 +2379,17 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png"); wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")), [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png"); + + menu->AppendSeparator(); + wxMenuItem* item_instance_to_object = append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")) + dots, _(L("Set an Instance as a Separate Object")), + [this](wxCommandEvent&) { q->instance_to_separated_object(); }, ""); + if (q != nullptr) { q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_set_number_of_copies->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_set_instance_to_object()); }, item_instance_to_object->GetId()); } menu->AppendSeparator(); @@ -2445,9 +2452,9 @@ bool Plater::priv::complit_init_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_volumes->GetId()); } return true; } @@ -2466,7 +2473,7 @@ bool Plater::priv::complit_init_sla_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); } return true; @@ -2485,7 +2492,7 @@ bool Plater::priv::complit_init_part_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); } return true; @@ -2553,6 +2560,12 @@ bool Plater::priv::can_increase_instances() const return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); } +bool Plater::priv::can_set_instance_to_object() const +{ + const int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); +} + bool Plater::priv::can_decrease_instances() const { int obj_idx = get_selected_object_idx(); @@ -2779,6 +2792,16 @@ void Plater::set_number_of_copies(/*size_t num*/) decrease_instances(-diff); } +void Plater::instance_to_separated_object() +{ + const int obj_idx = p->get_selected_object_idx(); + const int inst_idx = p->get_selection().get_instance_idx(); + if (obj_idx == -1 || inst_idx == -1) + return; + + sidebar().obj_list()->instance_to_separated_object(obj_idx, inst_idx); +} + bool Plater::is_selection_empty() const { return p->get_selection().is_empty(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7b19d6f31..a0655ed55 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -136,6 +136,7 @@ public: void increase_instances(size_t num = 1); void decrease_instances(size_t num = 1); void set_number_of_copies(/*size_t num*/); + void instance_to_separated_object(); bool is_selection_empty() const; 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);