diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index 21840aee2..cabc9bdfa 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -70,7 +70,7 @@ if(TBB_FOUND) # The Intel TBB library will use the std::exception_ptr feature of C++11. target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=1) - target_link_libraries(libnest2d INTERFACE ${TBB_LIBRARIES}) + target_link_libraries(libnest2d INTERFACE tbb) else() find_package(OpenMP QUIET) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index cc6f1c75e..331a42614 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -100,22 +100,33 @@ void PlaceholderParser::update_timestamp(DynamicConfig &config) // are expected to be addressed by the extruder ID, therefore // if a vector configuration value is addressed without an index, // a current extruder ID is used. -void PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) +bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) { const ConfigDef *def = rhs.def(); + bool modified = false; for (const t_config_option_key &opt_key : rhs.keys()) { const ConfigOptionDef *opt_def = def->get(opt_key); if ((opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process") continue; - const ConfigOption *opt = rhs.option(opt_key); + const ConfigOption *opt_rhs = rhs.option(opt_key); + const ConfigOption *opt_old = m_config.option(opt_key, false); + if (opt_old != nullptr) { + if ((opt_rhs->type() == coFloatOrPercent && + dynamic_cast(opt_old)->value == rhs.get_abs_value(opt_key)) || + *opt_rhs == *opt_old) + // no need to update + continue; + } // Store a copy of the config option. // Convert FloatOrPercent values to floats first. //FIXME there are some ratio_over chains, which end with empty ratio_with. // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - this->set(opt_key, (opt->type() == coFloatOrPercent) ? + this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : - opt->clone()); + opt_rhs->clone()); + modified = true; } + return modified; } void PlaceholderParser::apply_env_variables() diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index 49d53ec9e..d833969fc 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -14,7 +14,8 @@ class PlaceholderParser public: PlaceholderParser(); - void apply_config(const DynamicPrintConfig &config); + // Return true if modified. + bool apply_config(const DynamicPrintConfig &config); void apply_env_variables(); // Add new ConfigOption values to m_config. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d4fed0d5f..7815391e9 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -357,6 +357,14 @@ std::vector Print::extruders() const return extruders; } +unsigned int Print::num_object_instances() const +{ + unsigned int instances = 0; + for (const PrintObject *print_object : m_objects) + instances += print_object->copies().size(); + return instances; +} + void Print::_simplify_slices(double distance) { for (PrintObject *object : m_objects) { @@ -687,7 +695,7 @@ static std::vector print_objects_from_model_object(const ModelOb return std::vector(trafos.begin(), trafos.end()); } -bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) +Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in) { // Make a copy of the config, normalize it. DynamicPrintConfig config(config_in); @@ -697,14 +705,23 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) t_config_option_keys object_diff = m_default_object_config.diff(config); t_config_option_keys region_diff = m_default_region_config.diff(config); + // Do not use the ApplyStatus as we will use the max function when updating apply_status. + unsigned int apply_status = APPLY_STATUS_UNCHANGED; + auto update_apply_status = [&apply_status](bool invalidated) + { apply_status = std::max(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); }; + if (! (print_diff.empty() && object_diff.empty() && region_diff.empty())) + update_apply_status(false); + // Grab the lock for the Print / PrintObject milestones. tbb::mutex::scoped_lock lock(m_mutex); // The following call may stop the background processing. - bool invalidated = this->invalidate_state_by_config_options(print_diff); + update_apply_status(this->invalidate_state_by_config_options(print_diff)); // Apply variables to placeholder parser. The placeholder parser is used by G-code export, // which should be stopped if print_diff is not empty. - m_placeholder_parser.apply_config(config); + if (m_placeholder_parser.apply_config(config)) + update_apply_status(this->invalidate_step(psGCodeExport)); + // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. m_config.apply_only(config, print_diff, true); // Handle changes to object config defaults @@ -734,7 +751,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) // Kill everything, initialize from scratch. // Stop background processing. m_cancel_callback(); - this->invalidate_all_steps(); + update_apply_status(this->invalidate_all_steps()); for (PrintObject *object : m_objects) { model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); delete object; @@ -753,7 +770,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. - this->invalidate_step(psGCodeExport); + update_apply_status(this->invalidate_step(psGCodeExport)); for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { @@ -766,14 +783,14 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) m_cancel_callback(); this->invalidate_step(psGCodeExport); // Second create a new list of objects. - std::vector old(std::move(m_model.objects)); + std::vector model_objects_old(std::move(m_model.objects)); m_model.objects.clear(); m_model.objects.reserve(model.objects.size()); auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); }; - std::sort(old.begin(), old.end(), by_id_lower); + std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower); for (const ModelObject *mobj : model.objects) { - auto it = std::lower_bound(old.begin(), old.end(), mobj, by_id_lower); - if (it == old.end() || (*it)->id() != mobj->id()) { + auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower); + if (it == model_objects_old.end() || (*it)->id() != mobj->id()) { // New ModelObject added. m_model.objects.emplace_back((*it)->clone(&m_model)); model_object_status.emplace(mobj->id(), ModelObjectStatus::New); @@ -784,26 +801,30 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) } } bool deleted_any = false; - for (ModelObject *mobj : old) - if (model_object_status.find(ModelObjectStatus(mobj->id())) == model_object_status.end()) { - model_object_status.emplace(mobj->id(), ModelObjectStatus::Deleted); - delete mobj; + for (ModelObject *&model_object : model_objects_old) { + if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) { + model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted); deleted_any = true; - } + } else + // Do not delete this ModelObject instance. + model_object = nullptr; + } if (deleted_any) { // Delete PrintObjects of the deleted ModelObjects. - std::vector old = std::move(m_objects); + std::vector print_objects_old = std::move(m_objects); m_objects.clear(); - m_objects.reserve(old.size()); - for (PrintObject *print_object : old) { + m_objects.reserve(print_objects_old.size()); + for (PrintObject *print_object : print_objects_old) { auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); assert(it_status != model_object_status.end()); if (it_status->status == ModelObjectStatus::Deleted) { - print_object->invalidate_all_steps(); + update_apply_status(print_object->invalidate_all_steps()); delete print_object; } else m_objects.emplace_back(print_object); } + for (ModelObject *model_object : model_objects_old) + delete model_object; } } } @@ -860,7 +881,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) { - it->print_object->invalidate_all_steps(); + update_apply_status(it->print_object->invalidate_all_steps()); const_cast(*it).status = PrintObjectStatus::Deleted; } // Copy content of the ModelObject including its ID, reset the parent. @@ -872,7 +893,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) // Invalidate just the supports step. auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) - it->print_object->invalidate_step(posSupportMaterial); + update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); // Copy just the support volumes. model_volume_list_update_supports(model_object, model_object_new); } @@ -883,19 +904,23 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) this_object_config_diff = model_object.config.diff(model_object_new.config); if (! this_object_config_diff.empty()) model_object.config.apply_only(model_object_new.config, this_object_config_diff, true); - if (! this_object_config_diff.empty() || ! this_object_config_diff.empty()) { + if (! object_diff.empty() || ! this_object_config_diff.empty()) { PrintObjectConfig new_config = m_default_object_config; normalize_and_apply_config(new_config, model_object.config); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) { t_config_option_keys diff = it->print_object->config().diff(new_config); - invalidated |= it->print_object->invalidate_state_by_config_options(diff); - it->print_object->config_apply_only(new_config, diff, true); + if (! diff.empty()) { + update_apply_status(it->print_object->invalidate_state_by_config_options(diff)); + it->print_object->config_apply_only(new_config, diff, true); + } } } model_object.name = model_object_new.name; model_object.input_file = model_object_new.input_file; - model_object.instances = model_object_new.instances; + model_object.clear_instances(); + for (const ModelInstance *model_instance : model_object_new.instances) + model_object.add_instance(*model_instance); } } @@ -944,6 +969,9 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); } else if ((*it_old)->print_object->copies() != new_instances.copies) { // The PrintObject already exists and the copies differ. + if ((*it_old)->print_object->copies().size() != new_instances.copies.size()) + update_apply_status(this->invalidate_step(psWipeTower)); + update_apply_status(this->invalidate_step(psSkirt) || this->invalidate_step(psBrim) || this->invalidate_step(psGCodeExport)); (*it_old)->print_object->set_copies(new_instances.copies); print_objects_new.emplace_back((*it_old)->print_object); } @@ -952,6 +980,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) if (m_objects != print_objects_new) { m_cancel_callback(); m_objects = print_objects_new; + update_apply_status(false); } } @@ -998,7 +1027,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) } continue; print_object_end: - print_object->invalidate_all_steps(); + update_apply_status(print_object->invalidate_all_steps()); // Decrease the references to regions from this volume. int ireg = 0; for (const std::vector &volumes : print_object->region_volumes) { @@ -1014,7 +1043,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) region.config_apply_only(this_region_config, diff, false); for (PrintObject *print_object : m_objects) if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty()) - invalidated |= print_object->invalidate_state_by_config_options(diff); + update_apply_status(print_object->invalidate_state_by_config_options(diff)); } } } @@ -1072,7 +1101,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) object->update_layer_height_profile(); this->update_object_placeholders(); - return invalidated; + return static_cast(apply_status); } // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e765b8801..bd04cd371 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -94,6 +94,8 @@ public: printf("Not held!\n"); } #endif + // Raise the mutex, so that the following cancel() callback could cancel + // the background processing. mtx.unlock(); cancel(); m_state[step] = INVALID; @@ -393,7 +395,16 @@ public: bool reload_model_instances(); void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig config); - bool apply(const Model &model, const DynamicPrintConfig &config); + enum ApplyStatus { + // No change after the Print::apply() call. + APPLY_STATUS_UNCHANGED, + // Some of the Print / PrintObject / PrintObjectInstance data was changed, + // but no result was invalidated (only data influencing not yet calculated results were changed). + APPLY_STATUS_CHANGED, + // Some data was changed, which in turn invalidated already calculated steps. + APPLY_STATUS_INVALIDATED, + }; + ApplyStatus apply(const Model &model, const DynamicPrintConfig &config); void process(); void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); @@ -434,6 +445,9 @@ public: const PrintRegionPtrs& regions() const { return m_regions; } const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } + // How many of PrintObject::copies() over all print objects are there? + // If zero, then the print is empty and the print shall not be executed. + unsigned int num_object_instances() const; // Returns extruder this eec should be printed with, according to PrintRegion config: static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 71250598b..3ed5d6bfc 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -132,6 +132,7 @@ bool BackgroundSlicingProcess::start() if (! this->idle()) throw std::runtime_error("Cannot start a background task, the worker thread is not idle."); m_state = STATE_STARTED; + m_print->set_cancel_callback([this](){ this->stop(); }); lck.unlock(); m_condition.notify_one(); return true; @@ -151,9 +152,11 @@ bool BackgroundSlicingProcess::stop() m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); // In the "Canceled" state. Reset the state to "Idle". m_state = STATE_IDLE; + m_print->set_cancel_callback([](){}); } else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) { // In the "Finished" or "Canceled" state. Reset the state to "Idle". m_state = STATE_IDLE; + m_print->set_cancel_callback([](){}); } // this->m_export_path.clear(); return true; @@ -170,10 +173,10 @@ bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config) // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. -bool BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfig &config) +Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfig &config) { - this->stop(); - bool invalidated = m_print->apply(model, config); +// this->stop(); + Print::ApplyStatus invalidated = m_print->apply(model, config); return invalidated; } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 4265d1227..dab40d3a8 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -13,7 +13,6 @@ namespace Slic3r { class DynamicPrintConfig; class GCodePreviewData; class Model; -class Print; // Print step IDs for keeping track of the print state. enum BackgroundSlicingProcessStep { @@ -50,7 +49,7 @@ public: bool apply_config(const DynamicPrintConfig &config); // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. - bool apply(const Model &model, const DynamicPrintConfig &config); + Print::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config); // Set the export path of the G-code. // Once the path is set, the G-code void schedule_export(const std::string &path); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fa8368b4d..e39760184 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1647,35 +1647,32 @@ void Plater::priv::async_apply_config() this->q->model().update_print_volume_state(print_volume); // Apply new config to the possibly running background task. - bool was_running = this->background_process.running(); - bool invalidated = this->background_process.apply(this->q->model(), std::move(config)); + Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), std::move(config)); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. if (Slic3r::_3DScene::is_layers_editing_enabled(this->canvas3D)) this->canvas3D->Refresh(); - // If the apply_config caused the calculation to stop, or it was not running yet: - if (invalidated) { - if (was_running) { - // Hide the slicing results if the current slicing status is no more valid. - this->sidebar->show_info_sizers(false); - } - if (this->get_config("background_processing") == "1") - this->background_process.start(); - if (was_running) { - // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. - // Otherwise they will be just refreshed. - this->gcode_preview_data.reset(); - if (this->preview != nullptr) - this->preview->reload_print(); - // We also need to reload 3D scene because of the wipe tower preview box - if (this->config->opt_bool("wipe_tower")) { + + if (invalidated == Print::APPLY_STATUS_INVALIDATED) { + // Some previously calculated data on the Print was invalidated. + // Hide the slicing results, as the current slicing status is no more valid. + this->sidebar->show_info_sizers(false); + // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. + // Otherwise they will be just refreshed. + this->gcode_preview_data.reset(); + if (this->preview != nullptr) + this->preview->reload_print(); + // We also need to reload 3D scene because of the wipe tower preview box + if (this->config->opt_bool("wipe_tower")) { #if !ENABLE_EXTENDED_SELECTION - std::vector selections = this->collect_selections(); - Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections); - Slic3r::_3DScene::reload_scene(this->canvas3D, 1); + std::vector selections = this->collect_selections(); + Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections); + Slic3r::_3DScene::reload_scene(this->canvas3D, 1); #endif /* !ENABLE_EXTENDED_SELECTION */ - } } } + if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->get_config("background_processing") == "1" && + this->print.num_object_instances() > 0) + this->background_process.start(); } void Plater::priv::start_background_process() @@ -1844,7 +1841,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) this->statusbar()->set_status_text(message); } - //$self->print_info_box_show(1); + this->sidebar->show_info_sizers(false); // this updates buttons status //$self->object_list_changed;