From 314f7dc6742abe325bac27be95a3136e4ea2cc4c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 21 Nov 2018 17:35:35 +0100 Subject: [PATCH] WIP: Synchronization of SLA background processing. --- src/libslic3r/Model.cpp | 65 +++++ src/libslic3r/Model.hpp | 16 ++ src/libslic3r/Print.cpp | 65 ----- src/libslic3r/Print.hpp | 5 +- src/libslic3r/PrintConfig.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 433 +++++++++++++++++++++++++++++++--- src/libslic3r/SLAPrint.hpp | 17 +- src/slic3r/GUI/3DScene.cpp | 12 +- 8 files changed, 501 insertions(+), 114 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3801c5b12..b6f2a09bf 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1515,6 +1515,71 @@ Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, boo } #endif // !ENABLE_MODELVOLUME_TRANSFORM +// Test whether the two models contain the same number of ModelObjects with the same set of IDs +// ordered in the same order. In that case it is not necessary to kill the background processing. +bool model_object_list_equal(const Model &model_old, const Model &model_new) +{ + if (model_old.objects.size() != model_new.objects.size()) + return false; + for (size_t i = 0; i < model_old.objects.size(); ++ i) + if (model_old.objects[i]->id() != model_new.objects[i]->id()) + return false; + return true; +} + +// Test whether the new model is just an extension of the old model (new objects were added +// to the end of the original list. In that case it is not necessary to kill the background processing. +bool model_object_list_extended(const Model &model_old, const Model &model_new) +{ + if (model_old.objects.size() >= model_new.objects.size()) + return false; + for (size_t i = 0; i < model_old.objects.size(); ++ i) + if (model_old.objects[i]->id() != model_new.objects[i]->id()) + return false; + return true; +} + +bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type) +{ + bool modifiers_differ = false; + size_t i_old, i_new; + for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) { + const ModelVolume &mv_old = *model_object_old.volumes[i_old]; + const ModelVolume &mv_new = *model_object_new.volumes[i_new]; + if (mv_old.type() != type) { + ++ i_old; + continue; + } + if (mv_new.type() != type) { + ++ i_new; + continue; + } + if (mv_old.id() != mv_new.id()) + return true; + //FIXME test for the content of the mesh! + +#if ENABLE_MODELVOLUME_TRANSFORM + if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) + return true; +#endif // ENABLE_MODELVOLUME_TRANSFORM + ++i_old; + ++ i_new; + } + for (; i_old < model_object_old.volumes.size(); ++ i_old) { + const ModelVolume &mv_old = *model_object_old.volumes[i_old]; + if (mv_old.type() == type) + // ModelVolume was deleted. + return true; + } + for (; i_new < model_object_new.volumes.size(); ++ i_new) { + const ModelVolume &mv_new = *model_object_new.volumes[i_new]; + if (mv_new.type() == type) + // ModelVolume was added. + return true; + } + return false; +} + #ifdef _DEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index bf6656cfd..e5ab39f13 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -23,6 +23,7 @@ class ModelMaterial; class ModelObject; class ModelVolume; class Print; +class SLAPrint; typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; @@ -252,6 +253,7 @@ public: protected: friend class Print; + friend class SLAPrint; // Called by Print::apply() to set the model pointer after making a copy. void set_model(Model *model) { m_model = model; } @@ -368,6 +370,7 @@ public: protected: friend class Print; + friend class SLAPrint; friend class ModelObject; explicit ModelVolume(const ModelVolume &rhs) = default; @@ -535,6 +538,7 @@ public: protected: friend class Print; + friend class SLAPrint; friend class ModelObject; explicit ModelInstance(const ModelInstance &rhs) = default; @@ -652,6 +656,18 @@ private: #undef MODELBASE_DERIVED_COPY_MOVE_CLONE #undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE +// Test whether the two models contain the same number of ModelObjects with the same set of IDs +// ordered in the same order. In that case it is not necessary to kill the background processing. +extern bool model_object_list_equal(const Model &model_old, const Model &model_new); + +// Test whether the new model is just an extension of the old model (new objects were added +// to the end of the original list. In that case it is not necessary to kill the background processing. +extern bool model_object_list_extended(const Model &model_old, const Model &model_new); + +// Test whether the new ModelObject contains a different set of volumes (or sorted in a different order) +// than the old ModelObject. +extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type); + #ifdef _DEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4e1685e45..b14132ad6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -550,71 +550,6 @@ exit_for_rearrange_regions: return invalidated; } -// Test whether the two models contain the same number of ModelObjects with the same set of IDs -// ordered in the same order. In that case it is not necessary to kill the background processing. -static inline bool model_object_list_equal(const Model &model_old, const Model &model_new) -{ - if (model_old.objects.size() != model_new.objects.size()) - return false; - for (size_t i = 0; i < model_old.objects.size(); ++ i) - if (model_old.objects[i]->id() != model_new.objects[i]->id()) - return false; - return true; -} - -// Test whether the new model is just an extension of the old model (new objects were added -// to the end of the original list. In that case it is not necessary to kill the background processing. -static inline bool model_object_list_extended(const Model &model_old, const Model &model_new) -{ - if (model_old.objects.size() >= model_new.objects.size()) - return false; - for (size_t i = 0; i < model_old.objects.size(); ++ i) - if (model_old.objects[i]->id() != model_new.objects[i]->id()) - return false; - return true; -} - -static inline bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type) -{ - bool modifiers_differ = false; - size_t i_old, i_new; - for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) { - const ModelVolume &mv_old = *model_object_old.volumes[i_old]; - const ModelVolume &mv_new = *model_object_new.volumes[i_new]; - if (mv_old.type() != type) { - ++ i_old; - continue; - } - if (mv_new.type() != type) { - ++ i_new; - continue; - } - if (mv_old.id() != mv_new.id()) - return true; - //FIXME test for the content of the mesh! - -#if ENABLE_MODELVOLUME_TRANSFORM - if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) - return true; -#endif // ENABLE_MODELVOLUME_TRANSFORM - ++i_old; - ++ i_new; - } - for (; i_old < model_object_old.volumes.size(); ++ i_old) { - const ModelVolume &mv_old = *model_object_old.volumes[i_old]; - if (mv_old.type() == type) - // ModelVolume was deleted. - return true; - } - for (; i_new < model_object_new.volumes.size(); ++ i_new) { - const ModelVolume &mv_new = *model_object_new.volumes[i_new]; - if (mv_new.type() == type) - // ModelVolume was added. - return true; - } - return false; -} - // Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new // in the exact order and with the same IDs. // It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 0f6c0afbd..b5c693ee4 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -131,9 +131,6 @@ public: SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_support_layer(int idx); - // methods for handling state - bool invalidate_state_by_config_options(const std::vector &opt_keys); - // To be used over the layer_height_profile of both the PrintObject and ModelObject // to initialize the height profile with the height ranges. bool update_layer_height_profile(std::vector &layer_height_profile) const; @@ -174,6 +171,8 @@ protected: bool invalidate_step(PrintObjectStep step); // Invalidates all PrintObject and Print steps. bool invalidate_all_steps(); + // Invalidate steps based on a set of parameters changed. + bool invalidate_state_by_config_options(const std::vector &opt_keys); private: void make_perimeters(); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f1d1c9784..a3c79f4a4 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -902,7 +902,7 @@ class SLAPrintObjectConfig : public StaticPrintConfig { STATIC_PRINT_CONFIG_CACHE(SLAPrintObjectConfig) public: - ConfigOptionFloat layer_height; + ConfigOptionFloat layer_height; // Radius in mm of the pointing side of the head. ConfigOptionFloat support_head_front_radius /*= 0.2*/; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c8f6c1409..e0f5f18b0 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -73,53 +73,296 @@ void SLAPrint::clear() m_objects.clear(); } +// Transformation without rotation around Z and without a shift by X and Y. +static Transform3d sla_trafo(const ModelObject &model_object) +{ + ModelInstance &model_instance = *model_object.instances.front(); + Vec3d offset = model_instance.get_offset(); + Vec3d rotation = model_instance.get_rotation(); + offset(0) = 0.; + offset(1) = 0.; + rotation(2) = 0.; + return Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror()); +} + +// List of instances, where the ModelInstance transformation is a composite of sla_trafo and the transformation defined by SLAPrintObject::Instance. +static std::vector sla_instances(const ModelObject &model_object) +{ + std::vector instances; + for (ModelInstance *model_instance : model_object.instances) + if (model_instance->is_printable()) { + instances.emplace_back(SLAPrintObject::Instance( + model_instance->id(), + Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)), + float(model_instance->get_rotation(Z)))); + } + return instances; +} + SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConfig &config_in) { -// if (m_objects.empty()) -// return APPLY_STATUS_UNCHANGED; +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ + + // Make a copy of the config, normalize it. + DynamicPrintConfig config(config_in); + config.normalize(); + // Collect changes to print config. + t_config_option_keys printer_diff = m_printer_config.diff(config); + t_config_option_keys material_diff = m_material_config.diff(config); + t_config_option_keys object_diff = m_default_object_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 (! (printer_diff.empty() && material_diff.empty() && object_diff.empty())) + update_apply_status(false); // Grab the lock for the Print / PrintObject milestones. tbb::mutex::scoped_lock lock(this->state_mutex()); - if (m_objects.empty() && model.objects.empty() && m_model.objects.empty()) - return APPLY_STATUS_UNCHANGED; - // Temporary: just to have to correct layer height for the rasterization - DynamicPrintConfig config(config_in); - config.normalize(); - m_material_config.initial_layer_height.set( - config.opt("initial_layer_height")); + // The following call may stop the background processing. + update_apply_status(this->invalidate_state_by_config_options(printer_diff)); + update_apply_status(this->invalidate_state_by_config_options(material_diff)); - // Temporary quick fix, just invalidate everything. - { - for (SLAPrintObject *print_object : m_objects) { - print_object->invalidate_all_steps(); - delete print_object; - } - m_objects.clear(); - this->invalidate_all_steps(); + // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. + m_printer_config.apply_only(config, printer_diff, true); + // Handle changes to material config. + m_material_config.apply_only(config, material_diff, true); + // Handle changes to object config defaults + m_default_object_config.apply_only(config, object_diff, true); - // Copy the model by value (deep copy), - // keep the Model / ModelObject / ModelInstance / ModelVolume IDs. + struct ModelObjectStatus { + enum Status { + Unknown, + Old, + New, + Moved, + Deleted, + }; + ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} + ModelID id; + Status status; + // Search by id. + bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } + }; + std::set model_object_status; + + // 1) Synchronize model objects. + if (model.id() != m_model.id()) { + // Kill everything, initialize from scratch. + // Stop background processing. + this->call_cancell_callback(); + update_apply_status(this->invalidate_all_steps()); + for (SLAPrintObject *object : m_objects) { + model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); + delete object; + } + m_objects.clear(); m_model.assign_copy(model); - // Generate new SLAPrintObjects. - for (ModelObject *model_object : m_model.objects) { - auto po = new SLAPrintObject(this, model_object); - - // po->m_config.layer_height.set(lh); - po->m_config.apply(config, true); - - m_objects.emplace_back(po); - for (ModelInstance *oinst : model_object->instances) { - Point tr = Point::new_scale(oinst->get_offset()(X), - oinst->get_offset()(Y)); - auto rotZ = float(oinst->get_rotation()(Z)); - po->m_instances.emplace_back(oinst->id(), tr, rotZ); + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::New); + } else { + if (model_object_list_equal(m_model, model)) { + // The object list did not change. + for (const ModelObject *model_object : m_model.objects) + 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. + update_apply_status(this->invalidate_step(slapsRasterize)); + 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) { + model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); + m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i])); + m_model.objects.back()->set_model(&m_model); + } + } else { + // Reorder the objects, add new objects. + // First stop background processing before shuffling or deleting the PrintObjects in the object list. + this->call_cancell_callback(); + update_apply_status(this->invalidate_step(slapsRasterize)); + // Second create a new list of 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(model_objects_old.begin(), model_objects_old.end(), by_id_lower); + for (const ModelObject *mobj : model.objects) { + 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(ModelObject::new_copy(*mobj)); + m_model.objects.back()->set_model(&m_model); + model_object_status.emplace(mobj->id(), ModelObjectStatus::New); + } else { + // Existing ModelObject re-added (possibly moved in the list). + m_model.objects.emplace_back(*it); + model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved); + } + } + bool deleted_any = false; + 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 print_objects_old = std::move(m_objects); + m_objects.clear(); + m_objects.reserve(print_objects_old.size()); + for (SLAPrintObject *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) { + 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; } } } - return APPLY_STATUS_INVALIDATED; + // 2) Map print objects including their transformation matrices. + struct PrintObjectStatus { + enum Status { + Unknown, + Deleted, + Reused, + New + }; + PrintObjectStatus(SLAPrintObject *print_object, Status status = Unknown) : + id(print_object->model_object()->id()), + print_object(print_object), + trafo(print_object->trafo()), + status(status) {} + PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} + // ID of the ModelObject & PrintObject + ModelID id; + // Pointer to the old PrintObject + SLAPrintObject *print_object; + // Trafo generated with model_object->world_matrix(true) + Transform3d trafo; + Status status; + // Search by id. + bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; } + }; + std::multiset print_object_status; + for (SLAPrintObject *print_object : m_objects) + print_object_status.emplace(PrintObjectStatus(print_object)); + + // 3) Synchronize ModelObjects & PrintObjects. + std::vector print_objects_new; + print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); + bool new_objects = false; + for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { + ModelObject &model_object = *m_model.objects[idx_model_object]; + auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); + assert(it_status != model_object_status.end()); + assert(it_status->status != ModelObjectStatus::Deleted); + if (it_status->status == ModelObjectStatus::New) + // PrintObject instances will be added in the next loop. + continue; + // Update the ModelObject instance, possibly invalidate the linked PrintObjects. + assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); + const ModelObject &model_object_new = *model.objects[idx_model_object]; + auto it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id())); + if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id()) + it_print_object_status = print_object_status.end(); + // Check whether a model part volume was added or removed, their transformations or order changed. + bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART); + bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() || + (! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new))); + if (model_parts_differ || sla_trafo_differs) { + // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. + if (it_print_object_status != print_object_status.end()) { + update_apply_status(it_print_object_status->print_object->invalidate_all_steps()); + const_cast(*it_print_object_status).status = PrintObjectStatus::Deleted; + } + // Copy content of the ModelObject including its ID, do not change the parent. + model_object.assign_copy(model_object_new); + } else { + // Synchronize Object's config. + bool object_config_changed = model_object.config != model_object_new.config; + if (object_config_changed) + model_object.config = model_object_new.config; + if (! object_diff.empty() || object_config_changed) { + SLAPrintObjectConfig new_config = m_default_object_config; + normalize_and_apply_config(new_config, model_object.config); + if (it_print_object_status != print_object_status.end()) { + t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config); + if (! diff.empty()) { + update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff)); + it_print_object_status->print_object->config_apply_only(new_config, diff, true); + } + } + } + if (model_object.sla_support_points != model_object_new.sla_support_points) { + model_object.sla_support_points = model_object_new.sla_support_points; + if (it_print_object_status != print_object_status.end()) + update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints)); + } + // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step. + model_object.name = model_object_new.name; + model_object.input_file = model_object_new.input_file; + model_object.clear_instances(); + model_object.instances.reserve(model_object_new.instances.size()); + for (const ModelInstance *model_instance : model_object_new.instances) { + model_object.instances.emplace_back(new ModelInstance(*model_instance)); + model_object.instances.back()->set_model_object(&model_object); + } + } + + std::vector new_instances = sla_instances(model_object); + if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) { + // The SLAPrintObject is already there. + if (new_instances != it_print_object_status->print_object->instances()) { + // Instances changed. + it_print_object_status->print_object->set_instances(new_instances); + update_apply_status(this->invalidate_step(slapsRasterize)); + } + print_objects_new.emplace_back(it_print_object_status->print_object); + const_cast(*it_print_object_status).status = PrintObjectStatus::Reused; + } else { + auto print_object = new SLAPrintObject(this, &model_object); + print_object->set_trafo(sla_trafo(model_object)); + print_object->set_instances(new_instances); + print_object->config_apply(config, true); + print_objects_new.emplace_back(print_object); + new_objects = true; + } + } + + if (m_objects != print_objects_new) { + this->call_cancell_callback(); + update_apply_status(this->invalidate_all_steps()); + m_objects = print_objects_new; + // Delete the PrintObjects marked as Unknown or Deleted. + bool deleted_objects = false; + for (auto &pos : print_object_status) + if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { + // update_apply_status(pos.print_object->invalidate_all_steps()); + delete pos.print_object; + deleted_objects = true; + } + update_apply_status(new_objects); + } + +#ifdef _DEBUG + check_model_ids_equal(m_model, model); +#endif /* _DEBUG */ + + return static_cast(apply_status); } void SLAPrint::process() @@ -498,6 +741,61 @@ void SLAPrint::process() set_status(100, L("Slicing done")); } +bool SLAPrint::invalidate_state_by_config_options(const std::vector &opt_keys) +{ + if (opt_keys.empty()) + return false; + + // Cache the plenty of parameters, which influence the final rasterization only, + // or they are only notes not influencing the rasterization step. + static std::unordered_set steps_rasterize = { + "exposure_time", + "initial_exposure_time", + "material_correction_printing", + "material_correction_curing", + "display_width", + "display_height", + "display_pixels_x", + "display_pixels_y", + "printer_correction" + }; + + static std::unordered_set steps_ignore = { + "bed_shape", + "max_print_height", + "printer_technology", + }; + + std::vector steps; + std::vector osteps; + bool invalidated = false; + + for (const t_config_option_key &opt_key : opt_keys) { + if (steps_rasterize.find(opt_key) != steps_rasterize.end()) { + // These options only affect the final rasterization, or they are just notes without influence on the output, + // so there is nothing to invalidate. + steps.emplace_back(slapsRasterize); + } else if (steps_ignore.find(opt_key) != steps_ignore.end()) { + // These steps have no influence on the output. Just ignore them. + } else if (opt_key == "initial_layer_height") { + steps.emplace_back(slapsRasterize); + osteps.emplace_back(slaposObjectSlice); + } else { + // All values should be covered. + assert(false); + } + } + + sort_remove_duplicates(steps); + for (SLAPrintStep step : steps) + invalidated |= this->invalidate_step(step); + sort_remove_duplicates(osteps); + for (SLAPrintObjectStep ostep : osteps) + for (SLAPrintObject *object : m_objects) + invalidated |= object->invalidate_step(ostep); + return invalidated; +} + SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): Inherited(print, model_object), m_stepmask(slaposCount, true) @@ -506,6 +804,75 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): SLAPrintObject::~SLAPrintObject() {} +// Called by SLAPrint::apply_config(). +// This method only accepts SLAPrintObjectConfig option keys. +bool SLAPrintObject::invalidate_state_by_config_options(const std::vector &opt_keys) +{ + if (opt_keys.empty()) + return false; + + std::vector steps; + bool invalidated = false; + for (const t_config_option_key &opt_key : opt_keys) { + if ( opt_key == "support_head_front_radius" + || opt_key == "support_head_penetration" + || opt_key == "support_head_back_radius" + || opt_key == "support_head_width" + || opt_key == "support_pillar_radius" + || opt_key == "support_base_radius" + || opt_key == "support_base_height" + || opt_key == "support_critical_angle" + || opt_key == "support_max_bridge_length" + || opt_key == "support_object_elevation") { + steps.emplace_back(slaposSupportTree); + } else if ( + opt_key == "pad_enable" + || opt_key == "pad_wall_thickness" + || opt_key == "pad_wall_height" + || opt_key == "pad_max_merge_distance" + || opt_key == "pad_edge_radius") { + steps.emplace_back(slaposBasePool); + } else { + // All keys should be covered. + assert(false); + } + } + + sort_remove_duplicates(steps); + for (SLAPrintObjectStep step : steps) + invalidated |= this->invalidate_step(step); + return invalidated; +} + +bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) +{ + bool invalidated = Inherited::invalidate_step(step); + // propagate to dependent steps + if (step == slaposObjectSlice) { + invalidated |= this->invalidate_all_steps(); + } else if (step == slaposSupportIslands) { + invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsRasterize); + } else if (step == slaposSupportPoints) { + invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsRasterize); + } else if (step == slaposSupportTree) { + invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsRasterize); + } else if (step == slaposBasePool) { + invalidated |= this->invalidate_step(slaposSliceSupports); + invalidated |= m_print->invalidate_step(slapsRasterize); + } else if (step == slaposSliceSupports) { + invalidated |= m_print->invalidate_step(slapsRasterize); + } + return invalidated; +} + +bool SLAPrintObject::invalidate_all_steps() +{ + return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); +} + double SLAPrintObject::get_elevation() const { double ret = m_config.support_object_elevation.getFloat(); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 67a1db21a..8f913b818 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -35,16 +35,18 @@ private: // Prevents erroneous use by other classes. using Inherited = _SLAPrintObjectBase; public: - const Transform3d& trafo() const { return m_trafo; } + const SLAPrintObjectConfig& config() const { return m_config; } + const Transform3d& trafo() const { return m_trafo; } struct Instance { Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {} + bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; } // ID of the corresponding ModelInstance. ModelID instance_id; // Slic3r::Point objects in scaled G-code coordinates Point shift; // Rotation along the Z axis, in radians. - float rotation; + float rotation; }; const std::vector& instances() const { return m_instances; } @@ -88,12 +90,14 @@ protected: { this->m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo) { m_trafo = trafo; m_trmesh_valid = false; } - bool set_instances(const std::vector &instances); + void set_instances(const std::vector &instances) { m_instances = instances; } // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint. bool invalidate_step(SLAPrintObjectStep step); + bool invalidate_all_steps(); + // Invalidate steps based on a set of parameters changed. + bool invalidate_state_by_config_options(const std::vector &opt_keys); private: - // Object specific configuration, pulled from the configuration layer. SLAPrintObjectConfig m_config; // Translation in Z + Rotation by Y and Z + Scaling / Mirroring. @@ -150,8 +154,13 @@ private: using SLAPrinter = FilePrinter; using SLAPrinterPtr = std::unique_ptr; + // Invalidate steps based on a set of parameters changed. + bool invalidate_state_by_config_options(const std::vector &opt_keys); + SLAPrinterConfig m_printer_config; SLAMaterialConfig m_material_config; + SLAPrintObjectConfig m_default_object_config; + PrintObjects m_objects; std::vector m_stepmask; SLAPrinterPtr m_printer; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index b6b5691a1..3fc7ca82a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -822,16 +822,13 @@ void GLVolumeCollection::load_object_auxiliary( bool use_VBOs) { assert(print_object->is_step_done(milestone)); + Transform3d mesh_trafo_inv = print_object->trafo().inverse(); // Get the support mesh. - TriangleMesh mesh; - switch (milestone) { - case slaposSupportTree: mesh = print_object->support_mesh(); break; - case slaposBasePool: mesh = print_object->pad_mesh(); break; - default: - assert(false); - } + TriangleMesh mesh = print_object->get_mesh(milestone); + mesh.transform(mesh_trafo_inv); // Convex hull is required for out of print bed detection. TriangleMesh convex_hull = mesh.convex_hull_3d(); + convex_hull.transform(mesh_trafo_inv); for (const std::pair &instance_idx : instances) { const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first]; const SLAPrintObject::Instance &print_instance = print_object->instances()[instance_idx.second]; @@ -851,7 +848,6 @@ void GLVolumeCollection::load_object_auxiliary( v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true); v.is_modifier = false; v.shader_outside_printer_detection_enabled = true; - //FIXME adjust with print_instance? v.set_instance_transformation(model_instance.get_transformation()); // Leave the volume transformation at identity. // v.set_volume_transformation(model_volume->get_transformation());