mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-01 12:32:01 +08:00
Merge branch 'ms_multiple_beds_autoslice'
This commit is contained in:
commit
3db367be4a
@ -213,6 +213,7 @@ namespace ImGui
|
||||
const wchar_t PrintIdle = 0x2812;
|
||||
const wchar_t PrintRunning = 0x2813;
|
||||
const wchar_t PrintFinished = 0x2814;
|
||||
const wchar_t WarningMarkerDisabled = 0x2815;
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
70
resources/icons/notification_warning_grey.svg
Normal file
70
resources/icons/notification_warning_grey.svg
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
height="200"
|
||||
width="200"
|
||||
sodipodi:docname="notification_warning.svg"
|
||||
xml:space="preserve"
|
||||
enable-background="new 0 0 100 100"
|
||||
viewBox="0 0 200 200"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="warning"
|
||||
version="1.0"><metadata
|
||||
id="metadata19"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs17" /><sodipodi:namedview
|
||||
inkscape:current-layer="warning"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:cy="71.071558"
|
||||
inkscape:cx="34.775892"
|
||||
inkscape:zoom="3.5638182"
|
||||
showgrid="false"
|
||||
id="namedview15"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10"
|
||||
gridtolerance="10"
|
||||
objecttolerance="10"
|
||||
borderopacity="1"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff" />
|
||||
<g
|
||||
transform="matrix(2.2,0,0,2.2,-9.9977389,-10)"
|
||||
id="g4">
|
||||
<path
|
||||
id="path2"
|
||||
d="M 90,89.25 H 10 C 8.48,89.25 7.08,88.44 6.32,87.13 5.56,85.82 5.55,84.2 6.31,82.89 l 40,-70 c 0.76,-1.33 2.17,-2.14 3.69,-2.14 1.53,0 2.93,0.82 3.69,2.14 l 40,70 c 0.75,1.32 0.75,2.93 -0.01,4.24 -0.76,1.32 -2.16,2.12 -3.68,2.12 z M 17.33,80.75 H 82.68 L 50,23.57 Z"
|
||||
fill="#818181" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(2.2,0,0,2.2,-9.9977389,-10)"
|
||||
id="g8">
|
||||
<path
|
||||
id="path6"
|
||||
d="m 50,59.25 c -2.35,0 -4.25,-1.9 -4.25,-4.25 V 40 c 0,-2.35 1.9,-4.25 4.25,-4.25 2.35,0 4.25,1.9 4.25,4.25 v 15 c 0,2.35 -1.9,4.25 -4.25,4.25 z"
|
||||
fill="#818181" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(2.2,0,0,2.2,-9.9977389,-10)"
|
||||
id="g12">
|
||||
<circle
|
||||
id="circle10"
|
||||
r="5"
|
||||
cy="70"
|
||||
cx="50"
|
||||
fill="#818181" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
BIN
resources/icons/numbers.png
Normal file
BIN
resources/icons/numbers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 KiB |
@ -13,6 +13,19 @@ MultipleBeds s_multiple_beds;
|
||||
bool s_reload_preview_after_switching_beds = false;
|
||||
bool s_beds_just_switched = false;
|
||||
|
||||
bool is_sliceable(const PrintStatus status) {
|
||||
if (status == PrintStatus::empty) {
|
||||
return false;
|
||||
}
|
||||
if (status == PrintStatus::invalid) {
|
||||
return false;
|
||||
}
|
||||
if (status == PrintStatus::outside) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace BedsGrid {
|
||||
Index grid_coords_abs2index(GridCoords coords) {
|
||||
coords = {std::abs(coords.x()), std::abs(coords.y())};
|
||||
|
@ -29,6 +29,20 @@ inline std::vector<unsigned> s_bed_selector_thumbnail_texture_ids;
|
||||
inline std::array<bool, MAX_NUMBER_OF_BEDS> s_bed_selector_thumbnail_changed;
|
||||
inline bool bed_selector_updated{false};
|
||||
|
||||
enum class PrintStatus {
|
||||
idle,
|
||||
running,
|
||||
finished,
|
||||
outside,
|
||||
invalid,
|
||||
empty,
|
||||
toolpath_outside
|
||||
};
|
||||
|
||||
bool is_sliceable(const PrintStatus status);
|
||||
|
||||
inline std::array<PrintStatus, MAX_NUMBER_OF_BEDS> s_print_statuses;
|
||||
|
||||
class MultipleBeds {
|
||||
public:
|
||||
MultipleBeds() = default;
|
||||
|
@ -145,6 +145,58 @@ void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Tra
|
||||
mat.translate(s_multiple_beds.get_bed_translation(i));
|
||||
render_internal(canvas, mat, projection_matrix, bottom, scale_factor, show_texture, false, is_thumbnail || i == bed_to_highlight);
|
||||
}
|
||||
|
||||
if (m_digits_models.empty()) {
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
GLModel::Geometry g;
|
||||
g.format.vertex_layout = GLModel::Geometry::EVertexLayout::P3T2;
|
||||
const double digit_part = 94./1024.;
|
||||
g.add_vertex(Vec3f(0, 0, 0), Vec2f(digit_part * i, 1.));
|
||||
g.add_vertex(Vec3f(1, 0, 0), Vec2f(digit_part * (i+1), 1.));
|
||||
g.add_vertex(Vec3f(1, 1, 0), Vec2f(digit_part * (i+1), 0));
|
||||
g.add_vertex(Vec3f(0, 1, 0), Vec2f(digit_part * i, 0));
|
||||
g.add_triangle(0, 1, 3);
|
||||
g.add_triangle(3, 1, 2);
|
||||
m_digits_models.emplace_back(std::make_unique<GLModel>());
|
||||
m_digits_models.back()->init_from(std::move(g));
|
||||
m_digits_models.back()->set_color(ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f));
|
||||
}
|
||||
m_digits_texture = std::make_unique<GLTexture>();
|
||||
m_digits_texture->load_from_file(resources_dir() + "/icons/numbers.png", true, GLTexture::ECompressionType::None, false);
|
||||
m_digits_texture->send_compressed_data_to_gpu();
|
||||
}
|
||||
if (!is_thumbnail && s_multiple_beds.get_number_of_beds() > 1) {
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat_texture");
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", projection_matrix);
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_digits_texture->get_id()));
|
||||
|
||||
const BoundingBoxf bb = this->build_volume().bounding_volume2d();
|
||||
|
||||
for (int i : beds_to_render) {
|
||||
if (i + 1 >= m_digits_models.size())
|
||||
break;
|
||||
|
||||
double size_x = std::max(10., std::min(bb.size().x(), bb.size().y()) * 0.11);
|
||||
double aspect = 1.2;
|
||||
Transform3d mat = view_matrix;
|
||||
mat.translate(Vec3d(bb.min.x(), bb.min.y(), 0.));
|
||||
mat.translate(s_multiple_beds.get_bed_translation(i));
|
||||
if (build_volume().type() != BuildVolume::Type::Circle)
|
||||
mat.translate(Vec3d(0.3 * size_x, 0.3 * size_x, 0.));
|
||||
mat.translate(Vec3d(0., 0., 0.5));
|
||||
mat.scale(Vec3d(size_x, size_x * aspect, 1.));
|
||||
|
||||
shader->set_uniform("view_model_matrix", mat);
|
||||
m_digits_models[i + 1]->render();
|
||||
}
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor)
|
||||
@ -421,6 +473,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& v
|
||||
else if (m_texture.unsent_compressed_data_available()) {
|
||||
// sends to gpu the already available compressed levels of the main texture
|
||||
m_texture.send_compressed_data_to_gpu();
|
||||
wxQueueEvent(wxGetApp().plater(), new SimpleEvent(EVT_REGENERATE_BED_THUMBNAILS));
|
||||
|
||||
// the temporary texture is not needed anymore, reset it
|
||||
if (m_temp_texture.get_id() != 0)
|
||||
|
@ -54,6 +54,9 @@ private:
|
||||
|
||||
float m_scale_factor{ 1.0f };
|
||||
|
||||
std::vector<std::unique_ptr<GLModel>> m_digits_models;
|
||||
std::unique_ptr<GLTexture> m_digits_texture;
|
||||
|
||||
public:
|
||||
Bed3D() = default;
|
||||
~Bed3D() = default;
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@ -27,7 +27,7 @@ namespace GUI {
|
||||
|
||||
constexpr auto BORDER_W = 10;
|
||||
|
||||
void BulkExportDialog::Item::init_input_name_ctrl(wxBoxSizer* input_path_sizer, const std::string &path)
|
||||
void BulkExportDialog::Item::init_input_name_ctrl(wxFlexGridSizer* row_sizer, const std::string &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const long style = wxBORDER_SIMPLE;
|
||||
@ -37,31 +37,52 @@ void BulkExportDialog::Item::init_input_name_ctrl(wxBoxSizer* input_path_sizer,
|
||||
m_text_ctrl = new wxTextCtrl(m_parent, wxID_ANY, from_u8(path), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), style);
|
||||
wxGetApp().UpdateDarkUI(m_text_ctrl);
|
||||
m_text_ctrl->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); });
|
||||
m_text_ctrl->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& event) { event.Enable(selected); });
|
||||
|
||||
input_path_sizer->Add(m_text_ctrl, 1, wxEXPAND, BORDER_W);
|
||||
row_sizer->Add(m_text_ctrl, 1, wxEXPAND);
|
||||
}
|
||||
|
||||
void BulkExportDialog::Item::init_selection_ctrl(wxFlexGridSizer* row_sizer, int bed_index)
|
||||
{
|
||||
m_checkbox = new ::CheckBox(m_parent, std::to_string(bed_index + 1));
|
||||
m_checkbox->SetFont(wxGetApp().bold_font());
|
||||
wxGetApp().UpdateDarkUI(m_checkbox);
|
||||
m_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) {
|
||||
this->selected = event.IsChecked();
|
||||
});
|
||||
|
||||
row_sizer->Add(m_checkbox, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_checkbox->SetValue(this->selected);
|
||||
}
|
||||
|
||||
BulkExportDialog::Item::Item(
|
||||
wxWindow *parent,
|
||||
wxBoxSizer *sizer,
|
||||
const boost::filesystem::path &path,
|
||||
wxFlexGridSizer*sizer,
|
||||
const std::optional<const boost::filesystem::path>& path_opt,
|
||||
const int bed_index,
|
||||
Validator validator
|
||||
):
|
||||
path(path),
|
||||
bed_index(bed_index),
|
||||
m_parent(parent),
|
||||
m_valid_bmp(new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("tick_mark"))),
|
||||
m_valid_label(new wxStaticText(m_parent, wxID_ANY, "")),
|
||||
m_validator(std::move(validator)),
|
||||
m_directory(path.parent_path())
|
||||
m_validator(std::move(validator))
|
||||
{
|
||||
m_valid_label->SetFont(wxGetApp().bold_font());
|
||||
if (path_opt) {
|
||||
path = *path_opt;
|
||||
m_directory = path.parent_path();
|
||||
}
|
||||
|
||||
wxBoxSizer* input_path_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
input_path_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W);
|
||||
init_input_name_ctrl(input_path_sizer, path.filename().string());
|
||||
init_selection_ctrl(sizer, bed_index);
|
||||
init_input_name_ctrl(sizer, path.filename().string());
|
||||
sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, BORDER_W);
|
||||
|
||||
sizer->Add(input_path_sizer,0, wxEXPAND | wxTOP, BORDER_W);
|
||||
sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W);
|
||||
if (!path_opt) {
|
||||
m_checkbox->Enable(false);
|
||||
m_checkbox->SetValue(false);
|
||||
selected = false;
|
||||
}
|
||||
|
||||
m_valid_bmp->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& event) { event.Show(selected); });
|
||||
update();
|
||||
}
|
||||
|
||||
@ -157,13 +178,11 @@ void BulkExportDialog::Item::update()
|
||||
// for duplicates;
|
||||
const auto [status, info_line]{m_validator(path, filename)};
|
||||
|
||||
m_valid_label->SetLabel(info_line);
|
||||
m_valid_label->Show(!info_line.IsEmpty());
|
||||
m_valid_bmp->SetToolTip(info_line);
|
||||
|
||||
m_status = status;
|
||||
|
||||
update_valid_bmp();
|
||||
|
||||
m_parent->Layout();
|
||||
}
|
||||
|
||||
std::string get_bmp_name(const BulkExportDialog::ItemStatus status) {
|
||||
@ -181,11 +200,11 @@ void BulkExportDialog::Item::update_valid_bmp()
|
||||
m_valid_bmp->SetBitmap(*get_bmp_bundle(get_bmp_name(m_status)));
|
||||
}
|
||||
|
||||
BulkExportDialog::BulkExportDialog(const std::vector<fs::path> &paths):
|
||||
BulkExportDialog::BulkExportDialog(const std::vector<std::pair<int, std::optional<fs::path>>> &paths):
|
||||
DPIDialog(
|
||||
nullptr,
|
||||
wxID_ANY,
|
||||
paths.size() == 1 ? _L("Save bed") : _L("Save beds"),
|
||||
paths.size() == 1 ? _L("Export bed") : _L("Export beds"),
|
||||
wxDefaultPosition,
|
||||
wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()),
|
||||
wxDEFAULT_DIALOG_STYLE | wxICON_WARNING
|
||||
@ -199,15 +218,16 @@ BulkExportDialog::BulkExportDialog(const std::vector<fs::path> &paths):
|
||||
|
||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer = new wxFlexGridSizer(paths.size(), 3, wxSize(0.5*BORDER_W, BORDER_W));
|
||||
|
||||
for (const fs::path& path : paths) {
|
||||
AddItem(path);
|
||||
for (const auto&[bed_index, path] : paths) {
|
||||
AddItem(path, bed_index);
|
||||
}
|
||||
|
||||
// Add dialog's buttons
|
||||
wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||
wxButton* btnOK = static_cast<wxButton*>(this->FindWindowById(wxID_OK, this));
|
||||
btnOK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); });
|
||||
btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(enable_ok_btn()); });
|
||||
|
||||
topSizer->Add(m_sizer, 0, wxEXPAND | wxALL, BORDER_W);
|
||||
@ -224,41 +244,66 @@ BulkExportDialog::BulkExportDialog(const std::vector<fs::path> &paths):
|
||||
#endif
|
||||
}
|
||||
|
||||
void BulkExportDialog::AddItem(const fs::path& path)
|
||||
void BulkExportDialog::AddItem(const std::optional<const boost::filesystem::path>& path, int bed_index)
|
||||
{
|
||||
m_items.push_back(std::make_unique<Item>(this, m_sizer, path, PathValidator{m_items}));
|
||||
m_items.push_back(std::make_unique<Item>(this, m_sizer, path, bed_index, PathValidator{m_items}));
|
||||
}
|
||||
|
||||
void BulkExportDialog::accept()
|
||||
{
|
||||
if (has_warnings()) {
|
||||
MessageDialog dialog(nullptr,
|
||||
_L("Some of the selected files already exists. Do you want to replace them?"),
|
||||
_L("Export Beds"), wxYES_NO | wxICON_QUESTION);
|
||||
if (dialog.ShowModal() == wxID_NO)
|
||||
return;
|
||||
}
|
||||
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
|
||||
bool BulkExportDialog::enable_ok_btn() const
|
||||
{
|
||||
for (const auto &item : m_items)
|
||||
if (!item->is_valid()) {
|
||||
if (item->selected && !item->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
bool all_unselected{ true };
|
||||
for (const auto& item : m_items)
|
||||
if (item->selected) {
|
||||
all_unselected = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return !all_unselected;
|
||||
}
|
||||
|
||||
bool BulkExportDialog::Layout()
|
||||
{
|
||||
const bool ret = DPIDialog::Layout();
|
||||
this->Fit();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<boost::filesystem::path> BulkExportDialog::get_paths() const {
|
||||
std::vector<boost::filesystem::path> result;
|
||||
std::vector<std::pair<int, std::optional<fs::path>>> BulkExportDialog::get_paths() const {
|
||||
std::vector<std::pair<int, std::optional<fs::path>>> result;
|
||||
std::transform(
|
||||
m_items.begin(),
|
||||
m_items.end(),
|
||||
std::back_inserter(result),
|
||||
[](const auto &item){
|
||||
return item->path;
|
||||
[](const auto &item) -> std::pair<int, std::optional<fs::path>> {
|
||||
if (!item->selected) {
|
||||
return {item->bed_index, std::nullopt};
|
||||
}
|
||||
return {item->bed_index, item->path};
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BulkExportDialog::has_warnings() const
|
||||
{
|
||||
for (const auto& item : m_items)
|
||||
if (item->selected && item->is_warning()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BulkExportDialog::on_dpi_changed(const wxRect&)
|
||||
{
|
||||
const int& em = em_unit();
|
||||
|
@ -9,11 +9,15 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include <optional>
|
||||
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
|
||||
class wxString;
|
||||
class wxStaticText;
|
||||
class wxTextCtrl;
|
||||
class wxStaticBitmap;
|
||||
class wxFlexGridSizer;
|
||||
|
||||
namespace Slic3r {
|
||||
class Print;
|
||||
@ -36,8 +40,9 @@ public:
|
||||
>;
|
||||
Item(
|
||||
wxWindow *parent,
|
||||
wxBoxSizer *sizer,
|
||||
const boost::filesystem::path &path,
|
||||
wxFlexGridSizer *sizer,
|
||||
const std::optional<const boost::filesystem::path>& path,
|
||||
const int bed_index,
|
||||
Validator validator
|
||||
);
|
||||
Item(const Item &) = delete;
|
||||
@ -49,39 +54,45 @@ public:
|
||||
// directly to its address.
|
||||
|
||||
void update_valid_bmp();
|
||||
bool is_valid() const { return m_status != ItemStatus::NoValid; }
|
||||
bool is_valid() const { return m_status != ItemStatus::NoValid; }
|
||||
bool is_warning() const { return m_status == ItemStatus::Warning; }
|
||||
|
||||
boost::filesystem::path path;
|
||||
int bed_index{};
|
||||
bool selected{true};
|
||||
|
||||
private:
|
||||
ItemStatus m_status{ItemStatus::NoValid};
|
||||
wxWindow *m_parent{nullptr};
|
||||
wxStaticBitmap *m_valid_bmp{nullptr};
|
||||
wxTextCtrl *m_text_ctrl{nullptr};
|
||||
wxStaticText *m_valid_label{nullptr};
|
||||
::CheckBox *m_checkbox{nullptr};
|
||||
Validator m_validator;
|
||||
boost::filesystem::path m_directory{};
|
||||
|
||||
void init_input_name_ctrl(wxBoxSizer *input_name_sizer, const std::string &path);
|
||||
void init_input_name_ctrl(wxFlexGridSizer*row_sizer, const std::string &path);
|
||||
void init_selection_ctrl(wxFlexGridSizer*row_sizer, int bed_index);
|
||||
void update();
|
||||
};
|
||||
|
||||
private:
|
||||
// This must be a unique ptr, because Item does not have copy nor move constructors.
|
||||
std::vector<std::unique_ptr<Item>> m_items;
|
||||
wxBoxSizer *m_sizer{nullptr};
|
||||
wxFlexGridSizer*m_sizer{nullptr};
|
||||
|
||||
public:
|
||||
BulkExportDialog(const std::vector<boost::filesystem::path> &paths);
|
||||
bool Layout() override;
|
||||
std::vector<boost::filesystem::path> get_paths() const;
|
||||
|
||||
BulkExportDialog(const std::vector<std::pair<int, std::optional<boost::filesystem::path>>> &paths);
|
||||
std::vector<std::pair<int, std::optional<boost::filesystem::path>>> get_paths() const;
|
||||
bool has_warnings() const;
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &) override;
|
||||
void on_sys_color_changed() override {}
|
||||
|
||||
private:
|
||||
void AddItem(const boost::filesystem::path &path);
|
||||
void AddItem(const std::optional<const boost::filesystem::path>& path, int bed_index);
|
||||
void accept();
|
||||
bool enable_ok_btn() const;
|
||||
};
|
||||
|
||||
|
@ -1022,8 +1022,12 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
||||
});
|
||||
m_paths_bounding_box = BoundingBoxf3(libvgcode::convert(bbox[0]).cast<double>(), libvgcode::convert(bbox[1]).cast<double>());
|
||||
|
||||
if (wxGetApp().is_editor())
|
||||
if (wxGetApp().is_editor()) {
|
||||
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box);
|
||||
if (!m_contained_in_bed) {
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::toolpath_outside;
|
||||
}
|
||||
}
|
||||
|
||||
m_extruders_count = gcode_result.extruders_count;
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result);
|
||||
@ -1115,6 +1119,9 @@ void GCodeViewer::load_as_preview(libvgcode::GCodeInputData&& data)
|
||||
const libvgcode::AABox bbox = m_viewer.get_extrusion_bounding_box();
|
||||
const BoundingBoxf3 paths_bounding_box(libvgcode::convert(bbox[0]).cast<double>(), libvgcode::convert(bbox[1]).cast<double>());
|
||||
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(GCodeProcessorResult(), paths_bounding_box);
|
||||
if (!m_contained_in_bed) {
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::toolpath_outside;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeViewer::update_shells_color_by_extruder(const DynamicPrintConfig* config)
|
||||
|
@ -125,6 +125,8 @@ void GLCanvas3D::select_bed(int i, bool triggered_by_user)
|
||||
m_sequential_print_clearance.m_evaluating = true;
|
||||
reset_sequential_print_clearance();
|
||||
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, is_sliceable(s_print_statuses[i])));
|
||||
|
||||
// The stop call above schedules some events that would be processed after the switch.
|
||||
// Among else, on_process_completed would be called, which would stop slicing of
|
||||
// the new bed. We need to stop the process, pump all the events out of the queue
|
||||
@ -1560,8 +1562,9 @@ bool GLCanvas3D::check_volumes_outside_state(GLVolumeCollection& volumes, ModelI
|
||||
if (volume->printable) {
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding) {
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
contained_min_one |= !volume->is_outside;
|
||||
|
||||
if (bed_idx != -1 && bed_idx == s_multiple_beds.get_number_of_beds())
|
||||
@ -1827,7 +1830,7 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
|
||||
|
||||
using PerBedStatistics = std::vector<std::pair<
|
||||
std::size_t,
|
||||
std::reference_wrapper<const PrintStatistics>
|
||||
std::optional<std::reference_wrapper<const PrintStatistics>>
|
||||
>>;
|
||||
|
||||
PerBedStatistics get_statistics(){
|
||||
@ -1835,9 +1838,10 @@ PerBedStatistics get_statistics(){
|
||||
for (int bed_index=0; bed_index<s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
const Print* print = wxGetApp().plater()->get_fff_prints()[bed_index].get();
|
||||
if (print->empty() || !print->finished()) {
|
||||
continue;
|
||||
result.emplace_back(bed_index, std::nullopt);
|
||||
} else {
|
||||
result.emplace_back(bed_index, std::optional{std::ref(print->print_statistics())});
|
||||
}
|
||||
result.emplace_back(bed_index, std::ref(print->print_statistics()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1853,11 +1857,14 @@ struct StatisticsSum {
|
||||
StatisticsSum get_statistics_sum() {
|
||||
StatisticsSum result;
|
||||
for (const auto &[_, statistics] : get_statistics()) {
|
||||
result.cost += statistics.get().total_cost;
|
||||
result.filement_weight += statistics.get().total_weight;
|
||||
result.filament_length += statistics.get().total_used_filament;
|
||||
result.normal_print_time += statistics.get().normal_print_time_seconds;
|
||||
result.silent_print_time += statistics.get().silent_print_time_seconds;
|
||||
if (!statistics) {
|
||||
continue;
|
||||
}
|
||||
result.cost += statistics->get().total_cost;
|
||||
result.filement_weight += statistics->get().total_weight;
|
||||
result.filament_length += statistics->get().total_used_filament;
|
||||
result.normal_print_time += statistics->get().normal_print_time_seconds;
|
||||
result.silent_print_time += statistics->get().silent_print_time_seconds;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1897,20 +1904,37 @@ float project_overview_table(float scale) {
|
||||
);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &[bed_index, statistics] : get_statistics()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", (_u8L("Plate") + wxString::Format(" %d", bed_index + 1)).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_cost).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_weight).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_used_filament / 1000).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", statistics.get().estimated_silent_print_time.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", statistics.get().estimated_normal_print_time.c_str());
|
||||
for (const auto &[bed_index, optional_statistics] : get_statistics()) {
|
||||
if (optional_statistics) {
|
||||
const std::reference_wrapper<const PrintStatistics> statistics{*optional_statistics};
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", (_u8L("Bed") + wxString::Format(" %d", bed_index + 1)).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_cost).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_weight).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_used_filament / 1000).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", statistics.get().estimated_silent_print_time.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", statistics.get().estimated_normal_print_time.c_str());
|
||||
} else {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", (_u8L("Bed") + wxString::Format(" %d", bed_index + 1)).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("-");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiPureWrap::COL_ORANGE_LIGHT);
|
||||
@ -2211,22 +2235,20 @@ void GLCanvas3D::render()
|
||||
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
||||
m_rectangle_selection.render(*this);
|
||||
} else {
|
||||
const auto &prints{
|
||||
tcb::span{wxGetApp().plater()->get_fff_prints()}
|
||||
.subspan(0, s_multiple_beds.get_number_of_beds())
|
||||
};
|
||||
const auto &prints{wxGetApp().plater()->get_fff_prints()};
|
||||
|
||||
const bool all_finished{std::all_of(
|
||||
prints.begin(),
|
||||
prints.end(),
|
||||
[](const std::unique_ptr<Print> &print){
|
||||
return print->finished() || print->empty();
|
||||
bool all_finished{true};
|
||||
for (std::size_t bed_index{}; bed_index < s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
const std::unique_ptr<Print> &print{prints[bed_index]};
|
||||
if (!print->finished() && is_sliceable(s_print_statuses[bed_index])) {
|
||||
all_finished = false;
|
||||
break;
|
||||
}
|
||||
)};
|
||||
}
|
||||
|
||||
if (!all_finished) {
|
||||
render_autoslicing_wait();
|
||||
if (fff_print()->finished() || fff_print()->empty()) {
|
||||
if (fff_print()->finished() || !is_sliceable(s_print_statuses[s_multiple_beds.get_active_bed()])) {
|
||||
s_multiple_beds.autoslice_next_bed();
|
||||
wxYield();
|
||||
} else {
|
||||
@ -2847,7 +2869,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
// checks for geometry outside the print volume to render it accordingly
|
||||
if (!m_volumes.empty()) {
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
const bool contained_min_one = check_volumes_outside_state(m_volumes, &state, !force_full_scene_refresh);
|
||||
check_volumes_outside_state(m_volumes, &state, !force_full_scene_refresh);
|
||||
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
|
||||
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
|
||||
|
||||
@ -2870,15 +2892,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
||||
}
|
||||
}
|
||||
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
|
||||
contained_min_one && !m_model->objects.empty() && !partlyOut));
|
||||
}
|
||||
else {
|
||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||
_set_warning_notification(EWarning::ObjectClashed, false);
|
||||
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
||||
}
|
||||
|
||||
refresh_camera_scene_box();
|
||||
@ -6233,10 +6251,20 @@ void GLCanvas3D::_render_background()
|
||||
use_error_color = m_dynamic_background_enabled &&
|
||||
(current_printer_technology() != ptSLA || !m_volumes.empty());
|
||||
|
||||
if (!m_volumes.empty())
|
||||
use_error_color &= _is_any_volume_outside().first;
|
||||
else
|
||||
use_error_color &= m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
|
||||
if (s_multiple_beds.is_autoslicing()) {
|
||||
use_error_color &= std::any_of(
|
||||
s_print_statuses.begin(),
|
||||
s_print_statuses.end(),
|
||||
[](const PrintStatus status){
|
||||
return status == PrintStatus::toolpath_outside;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (!m_volumes.empty())
|
||||
use_error_color &= _is_any_volume_outside().first;
|
||||
else
|
||||
use_error_color &= m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a bottom to top gradient over the complete screen.
|
||||
@ -6581,17 +6609,15 @@ void GLCanvas3D::_render_overlays()
|
||||
|
||||
#define use_scrolling 1
|
||||
|
||||
enum class PrintStatus {
|
||||
idle,
|
||||
running,
|
||||
finished
|
||||
};
|
||||
|
||||
std::string get_status_text(PrintStatus status) {
|
||||
switch(status) {
|
||||
case PrintStatus::idle: return _u8L("Unsliced");
|
||||
case PrintStatus::running: return _u8L("Slicing...");
|
||||
case PrintStatus::running: return _u8L("Slicing") + "...";
|
||||
case PrintStatus::finished: return _u8L("Sliced");
|
||||
case PrintStatus::outside: return _u8L("Object at boundary");
|
||||
case PrintStatus::invalid: return _u8L("Invalid data");
|
||||
case PrintStatus::empty: return _u8L("Empty");
|
||||
case PrintStatus::toolpath_outside: return _u8L("Toolpath exceeds bounds");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -6601,6 +6627,10 @@ wchar_t get_raw_status_icon(const PrintStatus status) {
|
||||
case PrintStatus::finished: return ImGui::PrintFinished;
|
||||
case PrintStatus::running: return ImGui::PrintRunning;
|
||||
case PrintStatus::idle: return ImGui::PrintIdle;
|
||||
case PrintStatus::outside: return ImGui::PrintIdle;
|
||||
case PrintStatus::invalid: return ImGui::PrintIdle;
|
||||
case PrintStatus::empty: return ImGui::PrintIdle;
|
||||
case PrintStatus::toolpath_outside: return ImGui::PrintIdle;
|
||||
}
|
||||
return ImGui::PrintIdle;
|
||||
}
|
||||
@ -6615,13 +6645,14 @@ bool bed_selector_thumbnail(
|
||||
const float side,
|
||||
const float border,
|
||||
const float scale,
|
||||
const GLuint texture_id,
|
||||
const int bed_id,
|
||||
const std::optional<PrintStatus> status
|
||||
) {
|
||||
ImGuiWindow* window = GImGui->CurrentWindow;
|
||||
const ImVec2 current_position = GImGui->CurrentWindow->DC.CursorPos;
|
||||
const ImVec2 state_pos = current_position + ImVec2(3.f * border, side - 20.f * scale) * wxGetApp().imgui()->get_style_scaling();
|
||||
const ImVec2 state_pos = current_position + ImVec2(3.f * border, side - 20.f * scale);
|
||||
|
||||
const GLuint texture_id = s_bed_selector_thumbnail_texture_ids[bed_id];
|
||||
const bool clicked{ImGui::ImageButton(
|
||||
(void*)(int64_t)texture_id,
|
||||
size - padding,
|
||||
@ -6643,26 +6674,66 @@ bool bed_selector_thumbnail(
|
||||
);
|
||||
}
|
||||
|
||||
const ImVec2 id_pos = current_position + ImVec2(3.f * border, 1.5f * border);
|
||||
const std::string id = std::to_string(bed_id+1);
|
||||
|
||||
window->DrawList->AddText(
|
||||
GImGui->Font,
|
||||
GImGui->FontSize * 1.5f,
|
||||
id_pos,
|
||||
ImGui::GetColorU32(ImGuiCol_Text),
|
||||
id.c_str(),
|
||||
id.c_str() + id.size()
|
||||
);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
bool slice_all_beds_button(bool is_active, const ImVec2 size, const ImVec2 padding)
|
||||
bool button_with_icon(const wchar_t icon, const std::string& tooltip, bool is_active, const ImVec2 size)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGuiPureWrap::COL_GREY_DARK);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGuiPureWrap::COL_ORANGE_DARK);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGuiPureWrap::COL_ORANGE_DARK);
|
||||
std::string btn_name = boost::nowide::narrow(std::wstring{ icon });
|
||||
|
||||
ImGuiButtonFlags flags = ImGuiButtonFlags_None;
|
||||
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(btn_name.c_str());
|
||||
const ImFontAtlasCustomRect* const rect = wxGetApp().imgui()->GetTextureCustomRect(icon);
|
||||
const ImVec2 label_size = ImVec2(rect->Width, rect->Height);
|
||||
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect bb(pos, pos + size);
|
||||
ImGui::ItemSize(size, style.FramePadding.y);
|
||||
if (!ImGui::ItemAdd(bb, id))
|
||||
return false;
|
||||
|
||||
if (g.CurrentItemFlags & ImGuiItemFlags_ButtonRepeat)
|
||||
flags |= ImGuiButtonFlags_Repeat;
|
||||
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
|
||||
|
||||
// Render
|
||||
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiPureWrap::COL_ORANGE_DARK : hovered ? ImGuiPureWrap::COL_ORANGE_LIGHT : ImGuiPureWrap::COL_GREY_DARK);
|
||||
ImGui::RenderNavHighlight(bb, id);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, is_active ? ImGuiPureWrap::COL_BUTTON_ACTIVE : ImGuiPureWrap::COL_GREY_DARK);
|
||||
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
std::string slice_all_btn_name = boost::nowide::narrow(std::wstring{ ImGui::SliceAllBtnIcon });
|
||||
bool clicked = ImGui::Button(slice_all_btn_name.c_str(), size + padding);
|
||||
if (g.LogEnabled)
|
||||
ImGui::LogSetNextTextDecoration("[", "]");
|
||||
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, btn_name.c_str(), NULL, &label_size, style.ButtonTextAlign, &bb);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", _u8L("Slice all").c_str());
|
||||
}
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", tooltip.c_str());
|
||||
|
||||
return clicked;
|
||||
return pressed;
|
||||
}
|
||||
|
||||
void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
@ -6685,39 +6756,56 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
|
||||
auto render_bed_button = [btn_side, btn_border, btn_size, btn_padding, this, &extra_frame, scale](int i)
|
||||
{
|
||||
bool empty = ! s_multiple_beds.is_bed_occupied(i);
|
||||
bool inactive = i != s_multiple_beds.get_active_bed() || s_multiple_beds.is_autoslicing();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGuiPureWrap::COL_GREY_DARK);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, inactive ? ImGuiPureWrap::COL_GREY_DARK : ImGuiPureWrap::COL_BUTTON_ACTIVE);
|
||||
|
||||
if (empty)
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
const PrintStatus print_status{s_print_statuses[i]};
|
||||
|
||||
bool clicked = false;
|
||||
|
||||
std::optional<PrintStatus> print_status;
|
||||
if (current_printer_technology() == ptFFF) {
|
||||
print_status = PrintStatus::idle;
|
||||
if (wxGetApp().plater()->get_fff_prints()[i]->finished()) {
|
||||
print_status = PrintStatus::finished;
|
||||
} else if (m_process->fff_print() == wxGetApp().plater()->get_fff_prints()[i].get() && m_process->running()) {
|
||||
print_status = PrintStatus::running;
|
||||
if ( !previous_print_status[i]
|
||||
|| print_status != previous_print_status[i]
|
||||
) {
|
||||
extra_frame = true;
|
||||
}
|
||||
previous_print_status[i] = print_status;
|
||||
}
|
||||
|
||||
if (!previous_print_status[i] || print_status != previous_print_status[i]) {
|
||||
extra_frame = true;
|
||||
}
|
||||
previous_print_status[i] = print_status;
|
||||
|
||||
if (s_bed_selector_thumbnail_changed[i]) {
|
||||
extra_frame = true;
|
||||
s_bed_selector_thumbnail_changed[i] = false;
|
||||
}
|
||||
|
||||
if (i >= int(s_bed_selector_thumbnail_texture_ids.size()) || empty) {
|
||||
clicked = ImGui::Button(empty ? "empty" : std::to_string(i + 1).c_str(), btn_size + btn_padding);
|
||||
if (
|
||||
!is_sliceable(print_status)
|
||||
) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
}
|
||||
|
||||
bool clicked = false;
|
||||
if (
|
||||
!is_sliceable(print_status)
|
||||
) {
|
||||
clicked = button_with_icon(
|
||||
ImGui::WarningMarkerDisabled,
|
||||
get_status_text(print_status),
|
||||
!inactive,
|
||||
btn_size + btn_padding
|
||||
);
|
||||
} else if (print_status == PrintStatus::toolpath_outside) {
|
||||
clicked = button_with_icon(
|
||||
ImGui::WarningMarker,
|
||||
get_status_text(print_status),
|
||||
!inactive,
|
||||
btn_size + btn_padding
|
||||
);
|
||||
} else if (
|
||||
i >= int(s_bed_selector_thumbnail_texture_ids.size())
|
||||
) {
|
||||
clicked = ImGui::Button(
|
||||
std::to_string(i + 1).c_str(), btn_size + btn_padding
|
||||
);
|
||||
} else {
|
||||
clicked = bed_selector_thumbnail(
|
||||
btn_size,
|
||||
@ -6725,20 +6813,23 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
btn_side,
|
||||
btn_border,
|
||||
scale,
|
||||
s_bed_selector_thumbnail_texture_ids[i],
|
||||
print_status
|
||||
i,
|
||||
current_printer_technology() == ptFFF ? std::optional{print_status} : std::nullopt
|
||||
);
|
||||
}
|
||||
|
||||
if (clicked && ! empty)
|
||||
if (clicked && is_sliceable(print_status))
|
||||
select_bed(i, true);
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
if (empty)
|
||||
if (
|
||||
!is_sliceable(print_status)
|
||||
) {
|
||||
ImGui::PopItemFlag();
|
||||
}
|
||||
|
||||
if (print_status) {
|
||||
const std::string status_text{get_status_text(*print_status)};
|
||||
if (current_printer_technology() == ptFFF) {
|
||||
const std::string status_text{get_status_text(print_status)};
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", status_text.c_str());
|
||||
}
|
||||
@ -6782,7 +6873,7 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
|
||||
if (
|
||||
current_printer_technology() == ptFFF &&
|
||||
slice_all_beds_button(s_multiple_beds.is_autoslicing(), btn_size, btn_padding)
|
||||
button_with_icon(ImGui::SliceAllBtnIcon, _u8L("Slice all"), s_multiple_beds.is_autoslicing(), btn_size + btn_padding)
|
||||
) {
|
||||
if (!s_multiple_beds.is_autoslicing()) {
|
||||
s_multiple_beds.start_autoslice([this](int i, bool user) { this->select_bed(i, user); });
|
||||
|
@ -938,6 +938,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
||||
}
|
||||
|
||||
if (wxGetApp().is_editor() && !has_layers) {
|
||||
m_canvas->reset_gcode_toolpaths();
|
||||
m_canvas->reset_gcode_layers_times_cache();
|
||||
m_canvas->load_gcode_shells();
|
||||
hide_layers_slider();
|
||||
|
@ -135,6 +135,7 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||
{ImGui::EjectButton , "notification_eject_sd" },
|
||||
{ImGui::EjectHoverButton , "notification_eject_sd_hover" },
|
||||
{ImGui::WarningMarker , "notification_warning" },
|
||||
{ImGui::WarningMarkerDisabled , "notification_warning_grey" },
|
||||
{ImGui::ErrorMarker , "notification_error" },
|
||||
{ImGui::CancelButton , "notification_cancel" },
|
||||
{ImGui::CancelHoverButton , "notification_cancel_hover" },
|
||||
|
@ -170,6 +170,7 @@ wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent);
|
||||
// BackgroundSlicingProcess finished either with success or error.
|
||||
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, SlicingProcessCompletedEvent);
|
||||
wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_REGENERATE_BED_THUMBNAILS, SimpleEvent);
|
||||
|
||||
// Plater::DropTarget
|
||||
|
||||
@ -563,7 +564,7 @@ struct Plater::priv
|
||||
|
||||
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type);
|
||||
ThumbnailsList generate_thumbnails(const ThumbnailsParams& params, Camera::EType camera_type);
|
||||
void regenerate_thumbnails();
|
||||
void regenerate_thumbnails(SimpleEvent&);
|
||||
|
||||
void bring_instance_forward() const;
|
||||
|
||||
@ -581,8 +582,7 @@ struct Plater::priv
|
||||
std::string last_output_path;
|
||||
std::string last_output_dir_path;
|
||||
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
|
||||
bool process_completed_with_error { false };
|
||||
|
||||
|
||||
private:
|
||||
bool layers_height_allowed() const;
|
||||
|
||||
@ -772,6 +772,7 @@ void Plater::priv::init()
|
||||
q->Bind(EVT_EXPORT_BEGAN, &priv::on_export_began, this);
|
||||
q->Bind(EVT_GLVIEWTOOLBAR_3D, [this](SimpleEvent&) { q->select_view_3D("3D"); });
|
||||
q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [this](SimpleEvent&) { q->select_view_3D("Preview"); });
|
||||
q->Bind(EVT_REGENERATE_BED_THUMBNAILS, &priv::regenerate_thumbnails, this);
|
||||
}
|
||||
|
||||
// Drop target:
|
||||
@ -1891,10 +1892,39 @@ void Plater::priv::selection_changed()
|
||||
void Plater::priv::object_list_changed()
|
||||
{
|
||||
const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty());
|
||||
// XXX: is this right?
|
||||
const bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() == ModelInstancePVS_Inside;
|
||||
//
|
||||
if (printer_technology == ptFFF) {
|
||||
for (std::size_t bed_index{}; bed_index < s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
if (
|
||||
wxGetApp().plater()->get_fff_prints()[bed_index]->empty()) {
|
||||
s_print_statuses[bed_index] = PrintStatus::empty;
|
||||
}
|
||||
for (const ModelObject *object : wxGetApp().model().objects) {
|
||||
for (const ModelInstance *instance : object->instances) {
|
||||
const auto it{s_multiple_beds.get_inst_map().find(instance->id())};
|
||||
if (
|
||||
it != s_multiple_beds.get_inst_map().end()
|
||||
&& it->second == bed_index
|
||||
&& instance->printable
|
||||
&& instance->print_volume_state == ModelInstancePVS_Partly_Outside
|
||||
) {
|
||||
s_print_statuses[bed_index] = PrintStatus::outside;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (model.objects.empty()) {
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::empty;
|
||||
}
|
||||
}
|
||||
|
||||
sidebar->enable_buttons(s_multiple_beds.is_bed_occupied(s_multiple_beds.get_active_bed()) && !model.objects.empty() && !export_in_progress && model_fits);
|
||||
sidebar->enable_buttons(
|
||||
s_multiple_beds.is_bed_occupied(s_multiple_beds.get_active_bed())
|
||||
&& !export_in_progress
|
||||
&& is_sliceable(s_print_statuses[s_multiple_beds.get_active_bed()])
|
||||
);
|
||||
}
|
||||
|
||||
void Plater::priv::select_all()
|
||||
@ -2182,7 +2212,7 @@ std::vector<Print::ApplyStatus> apply_to_inactive_beds(
|
||||
return result;
|
||||
}
|
||||
|
||||
void Plater::priv::regenerate_thumbnails() {
|
||||
void Plater::priv::regenerate_thumbnails(SimpleEvent&) {
|
||||
const int num{s_multiple_beds.get_number_of_beds()};
|
||||
if (num <= 1 || num > MAX_NUMBER_OF_BEDS) {
|
||||
return;
|
||||
@ -2302,6 +2332,34 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
throw std::runtime_error{"Ivalid printer technology!"};
|
||||
}
|
||||
|
||||
for (std::size_t bed_index{}; bed_index < s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
if (printer_technology == ptFFF) {
|
||||
if (apply_statuses[bed_index] != Print::ApplyStatus::APPLY_STATUS_UNCHANGED) {
|
||||
s_print_statuses[bed_index] = PrintStatus::idle;
|
||||
}
|
||||
} else if (printer_technology == ptSLA) {
|
||||
if (apply_statuses[0] != Print::ApplyStatus::APPLY_STATUS_UNCHANGED) {
|
||||
s_print_statuses[bed_index] = PrintStatus::idle;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error{"Ivalid printer technology!"};
|
||||
}
|
||||
}
|
||||
|
||||
if (printer_technology == ptFFF) {
|
||||
for (std::size_t bed_index{0}; bed_index < q->p->fff_prints.size(); ++bed_index) {
|
||||
const std::unique_ptr<Print> &print{q->p->fff_prints[bed_index]};
|
||||
using MultipleBedsUtils::with_single_bed_model_fff;
|
||||
with_single_bed_model_fff(model, bed_index, [&](){
|
||||
std::vector<std::string> warnings;
|
||||
std::string err{print->validate(&warnings)};
|
||||
if (!err.empty()) {
|
||||
s_print_statuses[bed_index] = PrintStatus::invalid;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const bool any_status_changed{std::any_of(
|
||||
apply_statuses.begin(),
|
||||
apply_statuses.end(),
|
||||
@ -2316,7 +2374,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
|
||||
// If current bed was invalidated, update thumbnails for all beds:
|
||||
if (any_status_changed) {
|
||||
regenerate_thumbnails();
|
||||
wxQueueEvent(this->q, new SimpleEvent(EVT_REGENERATE_BED_THUMBNAILS));
|
||||
}
|
||||
|
||||
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
|
||||
@ -2346,7 +2404,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
notification_manager->set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
|
||||
if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! background_process.empty()) {
|
||||
// The delayed error message is no more valid.
|
||||
delayed_error_message.clear();
|
||||
@ -2410,7 +2467,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
return return_state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (! this->delayed_error_message.empty())
|
||||
// Reusing the old state.
|
||||
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
||||
@ -2423,7 +2480,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
actualize_slicing_warnings(*this->background_process.current_print());
|
||||
actualize_object_warnings(*this->background_process.current_print());
|
||||
show_warning_dialog = false;
|
||||
process_completed_with_error = false;
|
||||
}
|
||||
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
|
||||
@ -2439,7 +2495,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
const wxString invalid_str = _L("Invalid data");
|
||||
for (auto btn : {ActionButtonType::Reslice, ActionButtonType::SendGCode, ActionButtonType::Export})
|
||||
sidebar->set_btn_label(btn, invalid_str);
|
||||
process_completed_with_error = true;
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2455,9 +2511,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
const wxString slice_string = background_process.running() && wxGetApp().get_mode() == comSimple ?
|
||||
_L("Slicing") + dots : _L("Slice now");
|
||||
sidebar->set_btn_label(ActionButtonType::Reslice, slice_string);
|
||||
if (background_process.empty()) {
|
||||
sidebar->enable_buttons(false);
|
||||
} else if (background_process.finished())
|
||||
if (background_process.finished())
|
||||
show_action_buttons(false);
|
||||
else if (!background_process.empty() &&
|
||||
!background_process.running()) /* Do not update buttons if background process is running
|
||||
@ -2467,6 +2521,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
show_action_buttons(true);
|
||||
}
|
||||
|
||||
this->q->object_list_changed();
|
||||
return return_state;
|
||||
}
|
||||
|
||||
@ -3076,10 +3131,12 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
// see: Plater::priv::object_list_changed()
|
||||
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
|
||||
bool export_in_progress = this->background_process.is_export_scheduled();
|
||||
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
|
||||
if (s_multiple_beds.is_bed_occupied(s_multiple_beds.get_active_bed()) && !model.objects.empty() && !export_in_progress && model_fits) {
|
||||
if (
|
||||
s_multiple_beds.is_bed_occupied(s_multiple_beds.get_active_bed())
|
||||
&& !export_in_progress
|
||||
&& is_sliceable(s_print_statuses[s_multiple_beds.get_active_bed()])
|
||||
) {
|
||||
preview->get_canvas3d()->init_gcode_viewer();
|
||||
preview->get_canvas3d()->load_gcode_shells();
|
||||
q->reslice();
|
||||
@ -3258,6 +3315,7 @@ void Plater::priv::on_slicing_began()
|
||||
notification_manager->close_notification_of_type(NotificationType::SignDetected);
|
||||
notification_manager->close_notification_of_type(NotificationType::ExportFinished);
|
||||
notification_manager->set_slicing_progress_began();
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::running;
|
||||
}
|
||||
void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
|
||||
{
|
||||
@ -3349,15 +3407,19 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
const wxString invalid_str = _L("Invalid data");
|
||||
for (auto btn : { ActionButtonType::Reslice, ActionButtonType::SendGCode, ActionButtonType::Export })
|
||||
sidebar->set_btn_label(btn, invalid_str);
|
||||
process_completed_with_error = true;
|
||||
}
|
||||
has_error = true;
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::invalid;
|
||||
}
|
||||
if (evt.cancelled()) {
|
||||
this->notification_manager->set_slicing_progress_canceled(_u8L("Slicing Cancelled."));
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::idle;
|
||||
}
|
||||
|
||||
this->sidebar->show_sliced_info_sizer(evt.success());
|
||||
if (evt.success()) {
|
||||
s_print_statuses[s_multiple_beds.get_active_bed()] = PrintStatus::finished;
|
||||
}
|
||||
|
||||
// This updates the "Slice now", "Export G-code", "Arrange" buttons status.
|
||||
// Namely, it refreshes the "Out of print bed" property of all the ModelObjects, and it enables
|
||||
@ -4008,13 +4070,15 @@ void Plater::priv::show_autoslicing_action_buttons() const {
|
||||
sidebar->Layout();
|
||||
}
|
||||
|
||||
const bool all_finished{std::all_of(
|
||||
this->fff_prints.begin(),
|
||||
this->fff_prints.end(),
|
||||
[](const std::unique_ptr<Print> &print){
|
||||
return print->finished() || print->empty();
|
||||
bool all_finished{true};
|
||||
for (std::size_t bed_index{}; bed_index < s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
const std::unique_ptr<Print> &print{this->fff_prints[bed_index]};
|
||||
if (!print->finished() && is_sliceable(s_print_statuses[bed_index])) {
|
||||
all_finished = false;
|
||||
break;
|
||||
}
|
||||
)};
|
||||
}
|
||||
|
||||
sidebar->enable_bulk_buttons(all_finished);
|
||||
}
|
||||
|
||||
@ -4710,10 +4774,6 @@ void Plater::object_list_changed()
|
||||
p->object_list_changed();
|
||||
}
|
||||
|
||||
void Plater::regenerate_thumbnails() {
|
||||
p->regenerate_thumbnails();
|
||||
}
|
||||
|
||||
std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units /*= false*/) { return p->load_files(input_files, load_model, load_config, imperial_units); }
|
||||
|
||||
// To be called when providing a list of files to the GUI slic3r on command line.
|
||||
@ -5887,11 +5947,6 @@ std::optional<fs::path> Plater::get_multiple_output_dir(const std::string &start
|
||||
}
|
||||
|
||||
const fs::path output_path{into_path(dlg.GetPath())};
|
||||
if (auto error{check_output_path_has_error(output_path)}) {
|
||||
const t_link_clicked on_link_clicked = [](const std::string& key) -> void { wxGetApp().jump_to_option(key); };
|
||||
ErrorDialog(this, *error, on_link_clicked).ShowModal();
|
||||
return std::nullopt;
|
||||
}
|
||||
return output_path;
|
||||
}
|
||||
|
||||
@ -5904,7 +5959,7 @@ void Plater::export_gcode(bool prefer_removable)
|
||||
return;
|
||||
|
||||
|
||||
if (p->process_completed_with_error)
|
||||
if (!is_sliceable(s_print_statuses[s_multiple_beds.get_active_bed()]))
|
||||
return;
|
||||
|
||||
// If possible, remove accents from accented latin characters.
|
||||
@ -5956,15 +6011,40 @@ void Plater::export_gcode_to_path(
|
||||
struct PrintToExport {
|
||||
std::reference_wrapper<Slic3r::Print> print;
|
||||
std::reference_wrapper<Slic3r::GCodeProcessorResult> processor_result;
|
||||
boost::filesystem::path output_path;
|
||||
std::size_t bed{};
|
||||
int bed{};
|
||||
};
|
||||
|
||||
void Plater::with_mocked_fff_background_process(
|
||||
Print &print,
|
||||
GCodeProcessorResult &result,
|
||||
const int bed_index,
|
||||
const std::function<void()> &callable
|
||||
) {
|
||||
Print *original_print{&active_fff_print()};
|
||||
GCodeProcessorResult *original_result{this->p->background_process.get_gcode_result()};
|
||||
const int original_bed{s_multiple_beds.get_active_bed()};
|
||||
PrinterTechnology original_technology{this->printer_technology()};
|
||||
ScopeGuard guard{[&](){
|
||||
this->p->background_process.set_fff_print(original_print);
|
||||
this->p->background_process.set_gcode_result(original_result);
|
||||
this->p->background_process.select_technology(original_technology);
|
||||
s_multiple_beds.set_active_bed(original_bed);
|
||||
}};
|
||||
|
||||
this->p->background_process.set_fff_print(&print);
|
||||
this->p->background_process.set_gcode_result(&result);
|
||||
this->p->background_process.select_technology(this->p->printer_technology);
|
||||
s_multiple_beds.set_active_bed(bed_index);
|
||||
|
||||
callable();
|
||||
}
|
||||
|
||||
void Plater::export_all_gcodes(bool prefer_removable) {
|
||||
const auto optional_default_output_file{this->get_default_output_file()};
|
||||
if (!optional_default_output_file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fs::path &default_output_file{*optional_default_output_file};
|
||||
const std::string start_dir{get_output_start_dir(prefer_removable, default_output_file)};
|
||||
const auto optional_output_dir{get_multiple_output_dir(start_dir)};
|
||||
@ -5973,58 +6053,76 @@ void Plater::export_all_gcodes(bool prefer_removable) {
|
||||
}
|
||||
const fs_path &output_dir{*optional_output_dir};
|
||||
|
||||
std::vector<PrintToExport> prints_to_export;
|
||||
std::vector<fs::path> paths;
|
||||
|
||||
for (std::size_t print_index{0}; print_index < this->get_fff_prints().size(); ++print_index) {
|
||||
std::map<int, PrintToExport> prints_to_export;
|
||||
std::vector<std::pair< int, std::optional<fs::path> >> paths;
|
||||
|
||||
for (int print_index{0}; print_index < s_multiple_beds.get_number_of_beds(); ++print_index) {
|
||||
const std::unique_ptr<Print> &print{this->get_fff_prints()[print_index]};
|
||||
if (!print || print->empty()) {
|
||||
if (!print || !is_sliceable(s_print_statuses[print_index])) {
|
||||
paths.emplace_back(print_index, std::nullopt);
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::path default_filename{default_output_file.filename()};
|
||||
this->with_mocked_fff_background_process(
|
||||
*print,
|
||||
this->p->gcode_results[print_index],
|
||||
print_index,
|
||||
[&](){
|
||||
const auto optional_file{this->get_default_output_file()};
|
||||
if (!optional_file) {
|
||||
return;
|
||||
}
|
||||
const fs::path &default_file{*optional_file};
|
||||
default_filename = default_file.filename();
|
||||
}
|
||||
);
|
||||
|
||||
const fs::path filename{
|
||||
default_output_file.stem().string()
|
||||
default_filename.stem().string()
|
||||
+ "_bed"
|
||||
+ std::to_string(print_index + 1)
|
||||
+ default_output_file.extension().string()
|
||||
+ default_filename.extension().string()
|
||||
};
|
||||
const fs::path output_file{output_dir / filename};
|
||||
prints_to_export.push_back({*print, this->p->gcode_results[print_index], output_file, print_index});
|
||||
paths.push_back(output_file);
|
||||
prints_to_export.insert({
|
||||
print_index,
|
||||
{*print, this->p->gcode_results[print_index], print_index}
|
||||
});
|
||||
paths.emplace_back(print_index, output_file);
|
||||
}
|
||||
|
||||
BulkExportDialog dialog{paths};
|
||||
if (dialog.ShowModal() != wxID_OK) {
|
||||
return;
|
||||
}
|
||||
paths = dialog.get_paths();
|
||||
for (std::size_t path_index{0}; path_index < paths.size(); ++path_index) {
|
||||
prints_to_export[path_index].output_path = paths[path_index];
|
||||
}
|
||||
const std::vector<std::pair<int, std::optional<fs::path>>> output_paths{dialog.get_paths()};
|
||||
|
||||
bool path_on_removable_media{false};
|
||||
for (auto &[bed_index, optional_path] : output_paths) {
|
||||
if (!optional_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Print *original_print{&active_fff_print()};
|
||||
GCodeProcessorResult *original_result{this->p->background_process.get_gcode_result()};
|
||||
const int original_bed{s_multiple_beds.get_active_bed()};
|
||||
ScopeGuard guard{[&](){
|
||||
this->p->background_process.set_fff_print(original_print);
|
||||
this->p->background_process.set_gcode_result(original_result);
|
||||
s_multiple_beds.set_active_bed(original_bed);
|
||||
}};
|
||||
|
||||
for (const PrintToExport &print_to_export : prints_to_export) {
|
||||
this->p->background_process.set_fff_print(&print_to_export.print.get());
|
||||
this->p->background_process.set_gcode_result(&print_to_export.processor_result.get());
|
||||
this->p->background_process.set_temp_output_path(print_to_export.bed);
|
||||
export_gcode_to_path(
|
||||
print_to_export.output_path,
|
||||
[&](const bool on_removable){
|
||||
this->p->background_process.finalize_gcode(
|
||||
print_to_export.output_path.string(),
|
||||
path_on_removable_media
|
||||
const PrintToExport &print_to_export{prints_to_export.at(bed_index)};
|
||||
const fs::path &path{*optional_path};
|
||||
with_mocked_fff_background_process(
|
||||
print_to_export.print,
|
||||
print_to_export.processor_result,
|
||||
print_to_export.bed,
|
||||
[&](){
|
||||
this->p->background_process.set_temp_output_path(print_to_export.bed);
|
||||
export_gcode_to_path(
|
||||
path,
|
||||
[&](const bool on_removable){
|
||||
this->p->background_process.finalize_gcode(
|
||||
path.string(),
|
||||
path_on_removable_media
|
||||
);
|
||||
path_on_removable_media = on_removable || path_on_removable_media;
|
||||
}
|
||||
);
|
||||
path_on_removable_media = on_removable || path_on_removable_media;
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -6395,7 +6493,7 @@ void Plater::export_toolpaths_to_obj() const
|
||||
void Plater::reslice()
|
||||
{
|
||||
// There is "invalid data" button instead "slice now"
|
||||
if (p->process_completed_with_error)
|
||||
if (!is_sliceable(s_print_statuses[s_multiple_beds.get_active_bed()]))
|
||||
return;
|
||||
|
||||
// In case SLA gizmo is in editing mode, refuse to continue
|
||||
@ -6605,35 +6703,55 @@ void Plater::connect_gcode_all() {
|
||||
}
|
||||
const PrusaConnectNew connect{*print_host_ptr};
|
||||
|
||||
Print *original_print{&active_fff_print()};
|
||||
const int original_bed{s_multiple_beds.get_active_bed()};
|
||||
PrinterTechnology original_technology{this->printer_technology()};
|
||||
std::vector<std::pair< int, std::optional<fs::path> >> paths;
|
||||
|
||||
ScopeGuard guard{[&](){
|
||||
this->p->background_process.set_fff_print(original_print);
|
||||
s_multiple_beds.set_active_bed(original_bed);
|
||||
this->p->background_process.select_technology(original_technology);
|
||||
}};
|
||||
|
||||
for (std::size_t print_index{0}; print_index < this->get_fff_prints().size(); ++print_index) {
|
||||
for (std::size_t print_index{0}; print_index < s_multiple_beds.get_number_of_beds(); ++print_index) {
|
||||
const std::unique_ptr<Print> &print{this->get_fff_prints()[print_index]};
|
||||
if (!print || !is_sliceable(s_print_statuses[print_index])) {
|
||||
paths.emplace_back(print_index, std::nullopt);
|
||||
continue;
|
||||
}
|
||||
|
||||
const fs::path filename{upload_job_template.upload_data.upload_path};
|
||||
paths.emplace_back(print_index, fs::path{
|
||||
filename.stem().string()
|
||||
+ "_bed" + std::to_string(print_index + 1)
|
||||
+ filename.extension().string()
|
||||
});
|
||||
}
|
||||
|
||||
BulkExportDialog dialog{paths};
|
||||
if (dialog.ShowModal() != wxID_OK) {
|
||||
return;
|
||||
}
|
||||
const std::vector<std::pair<int, std::optional<fs::path>>> output_paths{dialog.get_paths()};
|
||||
|
||||
for (const auto &key_value : output_paths) {
|
||||
const int bed_index{key_value.first};
|
||||
const std::optional<fs::path> &optional_path{key_value.second};
|
||||
if (!optional_path) {
|
||||
continue;
|
||||
}
|
||||
const fs::path &path{*optional_path};
|
||||
|
||||
const std::unique_ptr<Print> &print{this->get_fff_prints()[bed_index]};
|
||||
if (!print || print->empty()) {
|
||||
continue;
|
||||
}
|
||||
this->p->background_process.set_fff_print(print.get());
|
||||
this->p->background_process.set_temp_output_path(print_index);
|
||||
this->p->background_process.select_technology(this->p->printer_technology);
|
||||
|
||||
PrintHostJob upload_job;
|
||||
upload_job.upload_data = upload_job_template.upload_data;
|
||||
upload_job.printhost = std::make_unique<PrusaConnectNew>(connect);
|
||||
upload_job.cancelled = upload_job_template.cancelled;
|
||||
const fs::path filename{upload_job.upload_data.upload_path};
|
||||
upload_job.upload_data.upload_path =
|
||||
filename.stem().string()
|
||||
+ "_bed" + std::to_string(print_index + 1)
|
||||
+ filename.extension().string();
|
||||
this->p->background_process.prepare_upload(upload_job);
|
||||
with_mocked_fff_background_process(
|
||||
*print,
|
||||
this->p->gcode_results[bed_index],
|
||||
bed_index,
|
||||
[&](){
|
||||
this->p->background_process.set_temp_output_path(bed_index);
|
||||
PrintHostJob upload_job;
|
||||
upload_job.upload_data = upload_job_template.upload_data;
|
||||
upload_job.printhost = std::make_unique<PrusaConnectNew>(connect);
|
||||
upload_job.cancelled = upload_job_template.cancelled;
|
||||
upload_job.upload_data.upload_path = path;
|
||||
this->p->background_process.prepare_upload(upload_job);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ namespace UndoRedo {
|
||||
namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_REGENERATE_BED_THUMBNAILS, SimpleEvent);
|
||||
|
||||
class MainFrame;
|
||||
class GLCanvas3D;
|
||||
@ -122,7 +123,6 @@ public:
|
||||
void reload_print();
|
||||
void object_list_changed();
|
||||
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type);
|
||||
void regenerate_thumbnails();
|
||||
|
||||
std::vector<size_t> load_files(const std::vector<boost::filesystem::path>& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false);
|
||||
// To be called when providing a list of files to the GUI slic3r on command line.
|
||||
@ -216,6 +216,12 @@ public:
|
||||
|
||||
void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
|
||||
|
||||
void with_mocked_fff_background_process(
|
||||
Print &print,
|
||||
GCodeProcessorResult &result,
|
||||
const int bed_index,
|
||||
const std::function<void()> &callable
|
||||
);
|
||||
void export_gcode(bool prefer_removable);
|
||||
void export_all_gcodes(bool prefer_removable);
|
||||
void export_stl_obj(bool extended = false, bool selection_only = false);
|
||||
|
@ -761,8 +761,9 @@ void Sidebar::on_select_preset(wxCommandEvent& evt)
|
||||
m_object_list->update_object_list_by_printer_technology();
|
||||
s_multiple_beds.stop_autoslice(false);
|
||||
this->switch_from_autoslicing_mode();
|
||||
this->m_plater->regenerate_thumbnails();
|
||||
wxQueueEvent(this->m_plater, new SimpleEvent(EVT_REGENERATE_BED_THUMBNAILS));
|
||||
this->m_plater->update();
|
||||
s_print_statuses.fill(PrintStatus::idle);
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
|
Loading…
x
Reference in New Issue
Block a user