Merge branch 'et_copy_and_paste' of https://github.com/prusa3d/Slic3r

This commit is contained in:
Enrico Turri 2019-04-12 12:40:25 +02:00
commit c5fa804c33
21 changed files with 546 additions and 75 deletions

View file

@ -721,32 +721,37 @@ int GLVolumeCollection::load_wipe_tower_preview(
return int(this->volumes.size() - 1);
}
typedef std::pair<GLVolume*, double> GLVolumeWithZ;
typedef std::vector<GLVolumeWithZ> GLVolumesWithZList;
static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
{
GLVolumesWithZList list;
GLVolumeWithIdAndZList list;
list.reserve(volumes.size());
for (GLVolume* volume : volumes)
for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i)
{
GLVolume* volume = volumes[i];
bool is_transparent = (volume->render_color[3] < 1.0f);
if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
(type == GLVolumeCollection::All)) &&
(! filter_func || filter_func(*volume)))
list.emplace_back(std::make_pair(volume, 0.0));
list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0)));
}
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
{
for (GLVolumeWithZ& volume : list)
for (GLVolumeWithIdAndZ& volume : list)
{
volume.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
volume.second.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
}
std::sort(list.begin(), list.end(),
[](const GLVolumeWithZ& v1, const GLVolumeWithZ& v2) -> bool { return v1.second < v2.second; }
[](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.second.second < v2.second.second; }
);
}
else if ((type == GLVolumeCollection::Opaque) && (list.size() > 1))
{
std::sort(list.begin(), list.end(),
[](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.first->selected && !v2.first->selected; }
);
}
@ -788,9 +793,8 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
if (clipping_plane_id != -1)
glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)clipping_plane));
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithZ& volume : to_render) {
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color();
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
@ -819,8 +823,8 @@ void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface,
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithZ& volume : to_render)
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render)
{
volume.first->set_render_color();
volume.first->render_legacy();

View file

@ -412,6 +412,8 @@ public:
};
typedef std::vector<GLVolume*> GLVolumePtrs;
typedef std::pair<GLVolume*, std::pair<unsigned int, double>> GLVolumeWithIdAndZ;
typedef std::vector<GLVolumeWithIdAndZ> GLVolumeWithIdAndZList;
class GLVolumeCollection
{
@ -509,6 +511,8 @@ private:
GLVolumeCollection& operator=(const GLVolumeCollection &);
};
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = nullptr);
class GLModel
{
protected:

View file

@ -3232,12 +3232,37 @@ bool GLCanvas3D::_init_toolbar()
if (!m_toolbar.add_separator())
return false;
item.name = "copy";
#if ENABLE_SVG_ICONS
item.icon_filename = "copy.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Copy") + " [" + GUI::shortkey_ctrl_prefix() + "C]";
item.sprite_id = 4;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy(); };
if (!m_toolbar.add_item(item))
return false;
item.name = "paste";
#if ENABLE_SVG_ICONS
item.icon_filename = "paste.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Paste") + " [" + GUI::shortkey_ctrl_prefix() + "V]";
item.sprite_id = 5;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste(); };
if (!m_toolbar.add_item(item))
return false;
if (!m_toolbar.add_separator())
return false;
item.name = "more";
#if ENABLE_SVG_ICONS
item.icon_filename = "instance_add.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Add instance [+]");
item.sprite_id = 4;
item.sprite_id = 6;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
@ -3249,7 +3274,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "instance_remove.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Remove instance [-]");
item.sprite_id = 5;
item.sprite_id = 7;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
@ -3264,7 +3289,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "split_objects.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Split to objects");
item.sprite_id = 6;
item.sprite_id = 8;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
@ -3276,7 +3301,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "split_parts.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Split to parts");
item.sprite_id = 7;
item.sprite_id = 9;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
@ -3291,7 +3316,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "layers.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Layers editing");
item.sprite_id = 8;
item.sprite_id = 10;
item.is_toggable = true;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
@ -3501,7 +3526,7 @@ void GLCanvas3D::_picking_pass() const
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
::glEnable(GL_CLIP_PLANE0);
}
_render_volumes(true);
_render_volumes_for_picking();
if (! m_use_VBOs)
::glDisable(GL_CLIP_PLANE0);
@ -3702,13 +3727,10 @@ void GLCanvas3D::_render_legend_texture() const
m_legend_texture.render(*this);
}
void GLCanvas3D::_render_volumes(bool fake_colors) const
void GLCanvas3D::_render_volumes_for_picking() const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
if (!fake_colors)
glsafe(::glEnable(GL_LIGHTING));
// do not cull backfaces to show broken geometry, if any
glsafe(::glDisable(GL_CULL_FACE));
@ -3718,27 +3740,31 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
unsigned int volume_id = 0;
for (GLVolume* vol : m_volumes.volumes)
const Transform3d& view_matrix = m_camera.get_view_matrix();
GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Opaque, view_matrix);
for (const GLVolumeWithIdAndZ& volume : to_render)
{
if (fake_colors)
{
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = (volume_id & 0x000000FF) >> 0;
unsigned int g = (volume_id & 0x0000FF00) >> 8;
unsigned int b = (volume_id & 0x00FF0000) >> 16;
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
}
else
{
vol->set_render_color();
glsafe(::glColor4fv(vol->render_color));
}
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = (volume.second.first & 0x000000FF) >> 0;
unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
if ((!fake_colors || !vol->disabled) && (vol->composite_id.volume_id >= 0 || m_render_sla_auxiliaries))
vol->render();
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
volume.first->render();
}
++volume_id;
to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Transparent, view_matrix);
for (const GLVolumeWithIdAndZ& volume : to_render)
{
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = (volume.second.first & 0x000000FF) >> 0;
unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
volume.first->render();
}
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
@ -3746,9 +3772,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_CULL_FACE));
if (!fake_colors)
glsafe(::glDisable(GL_LIGHTING));
}
void GLCanvas3D::_render_current_gizmo() const
@ -4026,15 +4049,9 @@ void GLCanvas3D::_update_volumes_hover_state() const
return;
GLVolume* volume = m_volumes.volumes[m_hover_volume_id];
switch (m_selection.get_mode())
{
case Selection::Volume:
{
if (volume->is_modifier)
volume->hover = true;
break;
}
case Selection::Instance:
else
{
int object_idx = volume->object_idx();
int instance_idx = volume->instance_idx();
@ -4044,9 +4061,6 @@ void GLCanvas3D::_update_volumes_hover_state() const
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
v->hover = true;
}
break;
}
}
}

View file

@ -609,7 +609,7 @@ private:
#endif // ENABLE_RENDER_SELECTION_CENTER
void _render_warning_texture() const;
void _render_legend_texture() const;
void _render_volumes(bool fake_colors) const;
void _render_volumes_for_picking() const;
void _render_current_gizmo() const;
void _render_gizmos_overlay() const;
void _render_toolbar() const;

View file

@ -21,6 +21,8 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);

View file

@ -20,6 +20,8 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);

View file

@ -439,6 +439,66 @@ void ObjectList::selection_changed()
part_selection_changed();
}
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
{
if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
return;
if (volumes.empty())
return;
ModelObject& model_object = *(*m_objects)[obj_idx];
const auto object_item = m_objects_model->GetItemById(obj_idx);
wxDataViewItemArray items;
for (const ModelVolume* volume : volumes)
{
auto vol_item = m_objects_model->AddVolumeChild(object_item, volume->name, volume->type(),
volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
auto opt_keys = volume->config.keys();
if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
select_item(m_objects_model->AddSettingsChild(vol_item));
items.Add(vol_item);
}
m_parts_changed = true;
parts_changed(obj_idx);
if (items.size() > 1)
{
m_selection_mode = smVolume;
m_last_selected_item = wxDataViewItem(0);
}
select_items(items);
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed();
#endif //no __WXOSX__ //__WXMSW__
}
void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
{
if (object_idxs.empty())
return;
wxDataViewItemArray items;
for (const size_t object : object_idxs)
{
add_object_to_list(object);
m_parts_changed = true;
parts_changed(object);
items.Add(m_objects_model->GetItemById(object));
}
select_items(items);
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed();
#endif //no __WXOSX__ //__WXMSW__
}
void ObjectList::OnChar(wxKeyEvent& event)
{
if (event.GetKeyCode() == WXK_BACK){
@ -1576,11 +1636,11 @@ void ObjectList::split()
for (auto id = 0; id < model_object->volumes.size(); id++) {
const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name),
model_object->volumes[id]->is_modifier() ?
ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
model_object->volumes[id]->config.has("extruder") ?
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
model_object->volumes[id]->is_modifier() ?
ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
model_object->volumes[id]->config.has("extruder") ?
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
// add settings to the part, if it has those
auto opt_keys = model_object->volumes[id]->config.keys();
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
@ -2135,6 +2195,7 @@ void ObjectList::update_selections_on_canvas()
add_to_selection(item, selection, instance_idx, false);
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
wxGetApp().plater()->canvas3D()->render();
}
void ObjectList::select_item(const wxDataViewItem& item)

View file

@ -31,6 +31,8 @@ typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
// category -> vector ( option ; label )
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
namespace GUI {
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
@ -285,6 +287,10 @@ public:
void rename_item();
void fix_through_netfabb() const;
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
private:
void OnChar(wxKeyEvent& event);
void OnContextMenu(wxDataViewEvent &event);

View file

@ -375,13 +375,22 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { m_plater->select_all(); }, "");
editMenu->AppendSeparator();
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete, _(L("Deletes the current selection")),
[this](wxCommandEvent&) { m_plater->remove_selected(); }, "");
[this](wxCommandEvent&) { m_plater->remove_selected(); }, "remove_menu");
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete, _(L("Deletes all objects")),
[this](wxCommandEvent&) { m_plater->reset(); }, "");
[this](wxCommandEvent&) { m_plater->reset(); }, "delete_all_menu");
editMenu->AppendSeparator();
wxMenuItem* item_copy = append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + "\tCtrl+C", _(L("Copy selection to clipboard")),
[this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, "copy_menu");
wxMenuItem* item_paste = append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + "\tCtrl+V", _(L("Paste clipboard")),
[this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, "paste_menu");
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete()); }, item_delete_sel->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_all()); }, item_delete_all->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater->can_copy()); }, item_copy->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater->can_paste()); }, item_paste->GetId());
}
// Window menu

View file

@ -1405,6 +1405,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
@ -3699,6 +3701,34 @@ void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/)
void Plater::update_object_menu() { p->update_object_menu(); }
void Plater::copy_selection_to_clipboard()
{
p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
}
void Plater::paste_from_clipboard()
{
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
}
bool Plater::can_paste_from_clipboard() const
{
const Selection& selection = p->view3D->get_canvas3d()->get_selection();
const Selection::Clipboard& clipboard = selection.get_clipboard();
Selection::EMode mode = clipboard.get_mode();
if (clipboard.is_empty())
return false;
if ((mode == Selection::Volume) && !selection.is_from_single_instance())
return false;
if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
return false;
return true;
}
bool Plater::can_delete() const { return p->can_delete(); }
bool Plater::can_delete_all() const { return p->can_delete_all(); }
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
@ -3707,5 +3737,7 @@ bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
bool Plater::can_arrange() const { return p->can_arrange(); }
bool Plater::can_layers_editing() const { return p->can_layers_editing(); }
bool Plater::can_copy() const { return !is_selection_empty(); }
bool Plater::can_paste() const { return can_paste_from_clipboard(); }
}} // namespace Slic3r::GUI

View file

@ -182,6 +182,10 @@ public:
PrinterTechnology printer_technology() const;
void set_printer_technology(PrinterTechnology printer_technology);
void copy_selection_to_clipboard();
void paste_from_clipboard();
bool can_paste_from_clipboard() const;
bool can_delete() const;
bool can_delete_all() const;
bool can_increase_instances() const;
@ -190,6 +194,8 @@ public:
bool can_split_to_volumes() const;
bool can_arrange() const;
bool can_layers_editing() const;
bool can_copy() const;
bool can_paste() const;
private:
struct priv;

View file

@ -1022,6 +1022,70 @@ bool Selection::requires_local_axes() const
return (m_mode == Volume) && is_from_single_instance();
}
void Selection::copy_to_clipboard()
{
if (!m_valid)
return;
m_clipboard.reset();
for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content)
{
ModelObject* src_object = m_model->objects[object.first];
ModelObject* dst_object = m_clipboard.add_object();
dst_object->name = src_object->name;
dst_object->input_file = src_object->input_file;
dst_object->config = src_object->config;
for (int i : object.second)
{
dst_object->add_instance(*src_object->instances[i]);
}
for (unsigned int i : m_list)
{
const GLVolume* volume = (*m_volumes)[i];
if ((volume->object_idx() == object.first) && (volume->instance_idx() == *object.second.begin()))
{
int volume_idx = volume->volume_idx();
if ((0 <= volume_idx) && (volume_idx < (int)src_object->volumes.size()))
{
ModelVolume* src_volume = src_object->volumes[volume_idx];
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
dst_volume->set_new_unique_id();
dst_volume->config = src_volume->config;
}
}
}
}
m_clipboard.set_mode(m_mode);
}
void Selection::paste_from_clipboard()
{
if (!m_valid || m_clipboard.is_empty())
return;
switch (m_clipboard.get_mode())
{
case Volume:
{
if (is_from_single_instance())
paste_volumes_from_clipboard();
break;
}
case Instance:
{
if (m_mode == Instance)
paste_objects_from_clipboard();
break;
}
}
}
void Selection::update_valid()
{
m_valid = (m_volumes != nullptr) && (m_model != nullptr);
@ -1697,5 +1761,43 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
return count == (unsigned int)m_model->objects[object_idx]->volumes.size();
}
void Selection::paste_volumes_from_clipboard()
{
int obj_idx = get_object_idx();
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
return;
ModelObject* src_object = m_clipboard.get_object(0);
if (src_object != nullptr)
{
ModelObject* dst_object = m_model->objects[obj_idx];
ModelVolumePtrs volumes;
for (ModelVolume* src_volume : src_object->volumes)
{
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
dst_volume->config = src_volume->config;
dst_volume->set_new_unique_id();
dst_volume->translate(10.0, 10.0, 0.0);
volumes.push_back(dst_volume);
}
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
}
}
void Selection::paste_objects_from_clipboard()
{
std::vector<size_t> object_idxs;
const ModelObjectPtrs& src_objects = m_clipboard.get_objects();
for (const ModelObject* src_object : src_objects)
{
ModelObject* dst_object = m_model->add_object(*src_object);
dst_object->translate(10.0, 10.0, 0.0);
object_idxs.push_back(m_model->objects.size() - 1);
}
wxGetApp().obj_list()->paste_objects_into_list(object_idxs);
}
} // namespace GUI
} // namespace Slic3r

View file

@ -138,6 +138,23 @@ public:
typedef std::set<int> InstanceIdxsList;
typedef std::map<int, InstanceIdxsList> ObjectIdxsToInstanceIdxsMap;
class Clipboard
{
Model m_model;
Selection::EMode m_mode;
public:
void reset() { m_model.clear_objects(); }
bool is_empty() const { return m_model.objects.empty(); }
ModelObject* add_object() { return m_model.add_object(); }
ModelObject* get_object(unsigned int id) { return (id < (unsigned int)m_model.objects.size()) ? m_model.objects[id] : nullptr; }
const ModelObjectPtrs& get_objects() const { return m_model.objects; }
Selection::EMode get_mode() const { return m_mode; }
void set_mode(Selection::EMode mode) { m_mode = mode; }
};
private:
struct Cache
{
@ -163,6 +180,7 @@ private:
// set of indices to m_volumes
IndicesList m_list;
Cache m_cache;
Clipboard m_clipboard;
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_dirty;
@ -267,6 +285,11 @@ public:
bool requires_local_axes() const;
void copy_to_clipboard();
void paste_from_clipboard();
const Clipboard& get_clipboard() const { return m_clipboard; }
private:
void update_valid();
void update_type();
@ -301,6 +324,9 @@ private:
void synchronize_unselected_volumes();
void ensure_on_bed();
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
void paste_volumes_from_clipboard();
void paste_objects_from_clipboard();
};
} // namespace GUI

View file

@ -529,10 +529,10 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ext
}
wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
{
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
if (!root) return wxDataViewItem(0);
@ -545,7 +545,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &pa
insert_position = -1;
if (create_frst_child && root->m_volumes_cnt == 0)
{
{
const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control

View file

@ -454,12 +454,12 @@ public:
~PrusaObjectDataViewModel();
wxDataViewItem Add(const wxString &name, const int extruder);
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);