diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f9df2649ca..833cd712e6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1147,7 +1147,7 @@ void ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu) void ObjectList::append_menu_item_export_stl(wxMenu* menu) const { append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, "", - [](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu); + [](wxCommandEvent&) { wxGetApp().plater()->export_stl(false, true); }, "", menu); menu->AppendSeparator(); } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 78d17a4f1a..72843f815d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -14,6 +14,7 @@ #include "libslic3r/Print.hpp" #include "libslic3r/Polygon.hpp" +#include "libslic3r/SLAPrint.hpp" #include "Tab.hpp" #include "PresetBundle.hpp" @@ -205,12 +206,30 @@ void MainFrame::add_created_tab(Tab* panel) bool MainFrame::can_save() const { - return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; + return (m_plater != nullptr) && !m_plater->model().objects.empty(); } bool MainFrame::can_export_model() const { - return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; + return (m_plater != nullptr) && !m_plater->model().objects.empty(); +} + +bool MainFrame::can_export_supports() const +{ + if ((m_plater == nullptr) || (m_plater->printer_technology() != ptSLA) || m_plater->model().objects.empty()) + return false; + + bool can_export = false; + const PrintObjects& objects = m_plater->sla_print().objects(); + for (const SLAPrintObject* object : objects) + { + if (object->has_mesh(slaposBasePool) || object->has_mesh(slaposSupportTree)) + { + can_export = true; + break; + } + } + return can_export; } bool MainFrame::can_export_gcode() const @@ -243,17 +262,17 @@ bool MainFrame::can_change_view() const bool MainFrame::can_select() const { - return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; + return (m_plater != nullptr) && !m_plater->model().objects.empty(); } bool MainFrame::can_delete() const { - return (m_plater != nullptr) ? !m_plater->is_selection_empty() : false; + return (m_plater != nullptr) && !m_plater->is_selection_empty(); } bool MainFrame::can_delete_all() const { - return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; + return (m_plater != nullptr) && !m_plater->model().objects.empty(); } void MainFrame::on_dpi_changed(const wxRect &suggested_rect) @@ -346,6 +365,8 @@ void MainFrame::init_menubar() export_menu->AppendSeparator(); wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater")); + wxMenuItem* item_export_stl_sla = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL including supports")) + dots, _(L("Export current plate as STL including supports")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, menu_icon("export_plater")); wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, menu_icon("export_plater")); export_menu->AppendSeparator(); @@ -389,13 +410,14 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { Close(false); }); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater != nullptr); }, item_open->GetId()); - Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_save()); }, item_save->GetId()); - Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_save()); }, item_save_as->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_save()); }, item_save->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_save()); }, item_save_as->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater != nullptr); }, item_import_model->GetId()); - Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_gcode()); }, item_export_gcode->GetId()); - Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_stl->GetId()); - Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_amf->GetId()); - Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_slice()); }, m_menu_item_reslice_now->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_gcode()); }, item_export_gcode->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_model()); }, item_export_stl->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_supports()); }, item_export_stl_sla->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_model()); }, item_export_amf->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_slice()); }, m_menu_item_reslice_now->GetId()); } #ifdef _MSC_VER diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 13bf07922f..f3d5826814 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -63,6 +63,7 @@ class MainFrame : public DPIFrame bool can_save() const; bool can_export_model() const; + bool can_export_supports() const; bool can_export_gcode() const; bool can_slice() const; bool can_change_view() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2195471daa..4ec560274f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2925,7 +2925,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ [this](wxCommandEvent&) { reload_from_disk(); }); append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")), - [this](wxCommandEvent&) { q->export_stl(true); }); + [this](wxCommandEvent&) { q->export_stl(false, true); }); menu->AppendSeparator(); } @@ -3431,7 +3431,7 @@ void Plater::export_gcode() p->export_gcode(std::move(output_path), PrintHostJob()); } -void Plater::export_stl(bool selection_only) +void Plater::export_stl(bool extended, bool selection_only) { if (p->model.objects.empty()) { return; } @@ -3466,8 +3466,65 @@ void Plater::export_stl(bool selection_only) } } else + { mesh = p->model.mesh(); + if (extended && (p->printer_technology == ptSLA)) + { + const PrintObjects& objects = p->sla_print.objects(); + for (const SLAPrintObject* object : objects) + { + const ModelObject* model_object = object->model_object(); + Transform3d mesh_trafo_inv = object->trafo().inverse(); + bool is_left_handed = object->is_left_handed(); + + TriangleMesh pad_mesh; + bool has_pad_mesh = object->has_mesh(slaposBasePool); + if (has_pad_mesh) + { + pad_mesh = object->get_mesh(slaposBasePool); + pad_mesh.transform(mesh_trafo_inv); + } + + TriangleMesh supports_mesh; + bool has_supports_mesh = object->has_mesh(slaposSupportTree); + if (has_supports_mesh) + { + supports_mesh = object->get_mesh(slaposSupportTree); + supports_mesh.transform(mesh_trafo_inv); + } + + const std::vector& obj_instances = object->instances(); + for (const SLAPrintObject::Instance& obj_instance : obj_instances) + { + auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), + [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); + assert(it != model_object->instances.end()); + + if (it != model_object->instances.end()) + { + int instance_idx = it - model_object->instances.begin(); + const Transform3d& inst_transform = object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); + + if (has_pad_mesh) + { + TriangleMesh inst_pad_mesh = pad_mesh; + inst_pad_mesh.transform(inst_transform, is_left_handed); + mesh.merge(inst_pad_mesh); + } + + if (has_supports_mesh) + { + TriangleMesh inst_supports_mesh = supports_mesh; + inst_supports_mesh.transform(inst_transform, is_left_handed); + mesh.merge(inst_supports_mesh); + } + } + } + } + } + } + Slic3r::store_stl(path_u8.c_str(), &mesh, true); p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index bedc31b352..326a4507e4 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -163,7 +163,7 @@ public: 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); void export_gcode(); - void export_stl(bool selection_only = false); + void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); void reslice();