Forbid seq arrange for a single bed in cases where it would reorder objects unexpectedly

This commit is contained in:
Lukas Matena 2025-02-13 01:35:00 +01:00
parent f2d0bc1f4f
commit 8180bea835
4 changed files with 74 additions and 7 deletions

View File

@ -18,6 +18,29 @@
namespace Slic3r {
static bool can_arrange_selected_bed(const Model& model, int bed_idx)
{
// When arranging a single bed, all instances of each object present must be on the same bed.
// Otherwise, the resulting order may not be possible to apply without messing up order
// on the other beds.
const auto map = s_multiple_beds.get_inst_map();
for (const ModelObject* mo : model.objects) {
std::map<int, bool> used_beds;
bool mo_on_this_bed = false;
for (const ModelInstance* mi : mo->instances) {
int id = -1;
if (auto it = map.find(mi->id()); it != map.end())
id = it->second;
if (id == bed_idx)
mo_on_this_bed = true;
used_beds[id] = true;
}
if (mo_on_this_bed && used_beds.size() != 1)
return false;
}
return true;
}
static Sequential::PrinterGeometry get_printer_geometry(const ConfigBase& config)
{
enum ShapeType {
@ -187,6 +210,9 @@ void arrange_model_sequential(Model& model, const ConfigBase& config, bool curre
SeqArrange::SeqArrange(const Model& model, const ConfigBase& config, bool current_bed_only)
{
m_selected_bed = current_bed_only ? s_multiple_beds.get_active_bed() : -1;
if (m_selected_bed != -1 && ! can_arrange_selected_bed(model, m_selected_bed))
throw ExceptionCannotAttemptSeqArrange();
m_printer_geometry = get_printer_geometry(config);
m_solver_configuration = get_solver_config(m_printer_geometry);
m_objects = get_objects_to_print(model, m_printer_geometry, m_selected_bed);
@ -201,6 +227,25 @@ void SeqArrange::process_seq_arrange(std::function<void(int)> progress_fn)
m_solver_configuration,
m_printer_geometry,
m_objects, progress_fn);
// If this was arrangement of a single bed, check that all instances of a single object
// ended up on the same bed. Otherwise we cannot apply the result (instances of a single
// object always follow one another in the object list and therefore the print).
if (m_selected_bed != -1 && s_multiple_beds.get_number_of_beds() > 1) {
int expected_plate = -1;
for (const Sequential::ObjectToPrint& otp : m_objects) {
auto it = std::find_if(m_plates.begin(), m_plates.end(), [&otp](const auto& plate)
{ return std::any_of(plate.scheduled_objects.begin(), plate.scheduled_objects.end(),
[&otp](const auto& obj) { return otp.id == obj.id;
});
});
assert(it != m_plates.end());
size_t plate_id = it - m_plates.begin();
if (expected_plate != -1 && expected_plate != plate_id)
throw ExceptionCannotApplySeqArrange();
expected_plate = otp.glued_to_next ? plate_id : -1;
}
}
}

View File

@ -10,10 +10,12 @@ namespace Slic3r {
class Model;
class ConfigBase;
class ExceptionCannotAttemptSeqArrange : public std::exception {};
class ExceptionCannotApplySeqArrange : public std::exception {};
void arrange_model_sequential(Model& model, const ConfigBase& config);
bool check_seq_printability(const Model& model, const ConfigBase& config);
// This is just a helper class to collect data for seq. arrangement, running the arrangement
// and applying the results to model. It is here so the processing itself can be offloaded
// into a separate thread without copying the Model or sharing it with UI thread.

View File

@ -7,6 +7,7 @@
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
@ -39,11 +40,24 @@ void SeqArrangeJob::process(Ctl& ctl)
void SeqArrangeJob::finalize(bool canceled, std::exception_ptr&)
void SeqArrangeJob::finalize(bool canceled, std::exception_ptr& eptr)
{
// If the task was cancelled, the stopping exception was already caught
// in 'process' function. Let any other exception propagate further.
if (! canceled) {
// in 'process' function. Any other exception propagates through here.
bool error = false;
if (eptr) {
try {
std::rethrow_exception(eptr);
} catch (const ExceptionCannotApplySeqArrange&) {
ErrorDialog dlg(wxGetApp().plater(), _L("The result of the single-bed arrange would scatter instances of a single object between several beds, "
"possibly affecting order of printing of the non-selected beds. Consider using global arrange across all beds."), false);
dlg.ShowModal();
error = true;
eptr = nullptr; // The exception is handled.
}
}
if (! canceled && ! error) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Arrange for sequential print"));
m_seq_arrange->apply_seq_arrange(wxGetApp().model());
wxGetApp().plater()->canvas3D()->reload_scene(true, true);

View File

@ -7122,8 +7122,14 @@ void Plater::arrange(bool current_bed_only)
const bool sequential = p->config->has("complete_objects") && p->config->opt_bool("complete_objects");
if (p->can_arrange()) {
if (sequential)
replace_job(this->get_ui_job_worker(), std::make_unique<SeqArrangeJob>(this->model(), *p->config, current_bed_only));
if (sequential) {
try {
replace_job(this->get_ui_job_worker(), std::make_unique<SeqArrangeJob>(this->model(), *p->config, current_bed_only));
} catch (const ExceptionCannotAttemptSeqArrange&) {
ErrorDialog dlg(this, _L("Sequential arrange for a single bed is only allowed when all instances of the affected objects are on the same bed."), false);
dlg.ShowModal();
}
}
else {
auto& w = get_ui_job_worker();
arrange(w, mode);