From bb1dfa352265da67d9678e83e4dfd951cc114d4b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 18 Oct 2018 14:36:46 +0200 Subject: [PATCH] WIP: Synchronization of Model / ModelObject with Print / PrintObject --- src/libslic3r/Print.cpp | 326 ++++++++++++++++++++++++++-------------- src/libslic3r/Print.hpp | 28 ++-- 2 files changed, 228 insertions(+), 126 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 82dc089d4..e309e265a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -376,6 +376,16 @@ double Print::max_allowed_layer_height() const return nozzle_diameter_max; } +static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume) +{ + PrintRegionConfig config = default_region_config; + normalize_and_apply_config(config, volume.get_object()->config); + normalize_and_apply_config(config, volume.config); + if (! volume.material_id().empty()) + normalize_and_apply_config(config, volume.material()->config); + return config; +} + // Caller is responsible for supplying models whose objects don't collide // and have explicit instance positions. void Print::add_model_object(ModelObject* model_object, int idx) @@ -397,7 +407,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) if (! volume->is_model_part() && ! volume->is_modifier()) continue; // Get the config applied to this volume. - PrintRegionConfig config = this->_region_config_from_model_volume(*volume); + PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume); // Find an existing print region with the same config. size_t region_id = size_t(-1); for (size_t i = 0; i < m_regions.size(); ++ i) @@ -424,35 +434,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) object->config_apply(src_normalized, true); } - // update placeholders - { - // get the first input file name - std::string input_file; - std::vector v_scale; - for (const PrintObject *object : m_objects) { - const ModelObject &mobj = *object->model_object(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // CHECK_ME -> Is the following correct ? - v_scale.push_back("x:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(X) * 100) + - "% y:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(Y) * 100) + - "% z:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(Z) * 100) + "%"); -#else - v_scale.push_back(boost::lexical_cast(mobj.instances[0]->scaling_factor * 100) + "%"); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - if (input_file.empty()) - input_file = mobj.input_file; - } - - PlaceholderParser &pp = m_placeholder_parser; - pp.set("scale", v_scale); - if (! input_file.empty()) { - // get basename with and without suffix - const std::string input_basename = boost::filesystem::path(input_file).filename().string(); - pp.set("input_filename", input_basename); - const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); - pp.set("input_filename_base", input_basename_base); - } - } + this->update_object_placeholders(); } bool Print::apply_config(DynamicPrintConfig config) @@ -514,12 +496,12 @@ bool Print::apply_config(DynamicPrintConfig config) // If the new config for this volume differs from the other // volume configs currently associated to this region, it means // the region subdivision does not make sense anymore. - if (! this_region_config.equals(this->_region_config_from_model_volume(volume))) { + if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume))) { rearrange_regions = true; goto exit_for_rearrange_regions; } } else { - this_region_config = this->_region_config_from_model_volume(volume); + this_region_config = region_config_from_model_volume(m_default_region_config, volume); this_region_config_set = true; } for (const PrintRegionConfig &cfg : other_region_configs) { @@ -681,7 +663,7 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d & struct PrintInstances { Transform3d trafo; - Points instances; + Points copies; bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } }; @@ -690,26 +672,46 @@ static std::vector print_objects_from_model_object(const ModelOb { std::set trafos; PrintInstances trafo; - trafo.instances.assign(1, Point()); + trafo.copies.assign(1, Point()); for (ModelInstance *model_instance : model_object.instances) if (model_instance->is_printable()) { const Vec3d &offst = model_instance->get_offset(); trafo.trafo = model_instance->world_matrix(true); - trafo.instances.front() = Point::new_scale(offst(0), offst(1)); + trafo.copies.front() = Point::new_scale(offst(0), offst(1)); auto it = trafos.find(trafo); if (it == trafos.end()) trafos.emplace(trafo); else - const_cast(*it).instances.emplace_back(trafo.instances.front()); + const_cast(*it).copies.emplace_back(trafo.copies.front()); } return std::vector(trafos.begin(), trafos.end()); } -bool Print::apply(const Model &model, const DynamicPrintConfig &config) +bool Print::apply(const Model &model, const DynamicPrintConfig &config_in) { + // Make a copy of the config, normalize it. + DynamicPrintConfig config(config_in); + config.normalize(); + // Collect changes to print config. + t_config_option_keys print_diff = m_config.diff(config); + t_config_option_keys object_diff = m_default_object_config.diff(config); + t_config_option_keys region_diff = m_default_region_config.diff(config); + // 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); + // 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); + // 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 + m_default_object_config.apply_only(config, object_diff, true); + // Handle changes to regions config defaults + m_default_region_config.apply_only(config, region_diff, true); + struct ModelObjectStatus { enum Status { Unknown, @@ -719,8 +721,9 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config) Deleted, }; ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} - ModelID id; - Status status; + ModelID id; + Status status; + t_config_option_keys object_config_diff; // Search by id. bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } }; @@ -729,7 +732,8 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config) // 1) Synchronize model objects. if (model.id() != m_model.id()) { // Kill everything, initialize from scratch. - // The following call shall kill any computation if running. + // Stop background processing. + m_cancel_callback(); this->invalidate_all_steps(); for (PrintObject *object : m_objects) { model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); @@ -746,6 +750,8 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config) } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. 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) { model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); m_model.objects.emplace_back(model.objects[i]->clone(&m_model)); @@ -770,7 +776,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config) } else { // Existing ModelObject re-added (possibly moved in the list). m_model.objects.emplace_back(*it); - model_object_status.emplace(mobj->id(), ModelObjectStatus::Old); + model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved); } } bool deleted_any = false; @@ -835,7 +841,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config) // PrintObject instances will be added in the next loop. continue; // Update the ModelObject instance, possibly invalidate the linked PrintObjects. - assert(it_status->status == ModelObjectStatus::Moved); + assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); const ModelObject &model_object_new = *model.objects[idx_model_object]; // 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); @@ -864,87 +870,187 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config) } if (! model_parts_differ && ! modifiers_differ) { // Synchronize the remaining data of ModelVolumes (name, config, m_type, m_material_id) + // Synchronize Object's config. + t_config_option_keys &this_object_config_diff = const_cast(*it_status).object_config_diff; + 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()) { + 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); + } + } } } // 4) Generate PrintObjects from ModelObjects and their instances. - std::vector print_objects_new; - print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); - // Walk over all new model objects and check, whether there are matching PrintObjects. - for (ModelObject *model_object : m_model.objects) { - auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id())); - std::vector old; - if (range.first != range.second) { - old.reserve(print_object_status.count(PrintObjectStatus(model_object->id()))); - for (auto it = range.first; it != range.second; ++ it) - if (it->status != PrintObjectStatus::Deleted) - old.emplace_back(&(*it)); + { + std::vector print_objects_new; + print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); + // Walk over all new model objects and check, whether there are matching PrintObjects. + for (ModelObject *model_object : m_model.objects) { + auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id())); + std::vector old; + if (range.first != range.second) { + old.reserve(print_object_status.count(PrintObjectStatus(model_object->id()))); + for (auto it = range.first; it != range.second; ++ it) + if (it->status != PrintObjectStatus::Deleted) + old.emplace_back(&(*it)); + } + // Generate a list of trafos and XY offsets for instances of a ModelObject + PrintObjectConfig config = m_default_object_config; + normalize_and_apply_config(config, model_object->config); + std::vector new_print_instances = print_objects_from_model_object(*model_object); + if (old.empty()) { + // Simple case, just generate new instances. + for (const PrintInstances &print_instances : new_print_instances) { + PrintObject *print_object = new PrintObject(this, model_object, model_object->raw_bounding_box()); + print_object->set_copies(print_instances.copies); + print_object->config_apply(config); + print_objects_new.emplace_back(print_object); + print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); + } + continue; + } + // Complex case, try to merge the two lists. + // Sort the old lexicographically by their trafos. + std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); + // Merge the old / new lists. + auto it_old = old.begin(); + for (const PrintInstances &new_instances : new_print_instances) { + for (; transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); + if (! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { + // This is a new instance (or a set of instances with the same trafo). Just add it. + PrintObject *print_object = new PrintObject(this, model_object, model_object->raw_bounding_box()); + print_object->set_copies(new_instances.copies); + print_object->config_apply(config); + print_objects_new.emplace_back(print_object); + 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. The only step currently sensitive to the order is the G-code generator. + // Stop it. + this->invalidate_step(psGCodeExport); + (*it_old)->print_object->set_copies(new_instances.copies); + } + } } - // Generate a list of trafos and XY offsets for instances of a ModelObject - std::vector new_print_instances = print_objects_from_model_object(*model_object); - if (old.empty()) { - // Simple case, just generate new instances. - for (const PrintInstances &print_instances : new_print_instances) { - PrintObject *print_object = new PrintObject(this, model_object, model_object->raw_bounding_box()); - print_objects_new.emplace_back(print_object); - print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); + if (m_objects != print_objects_new) { + m_cancel_callback(); + m_objects = print_objects_new; + } + } + + // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions. + // Update reference counts of regions from the remaining PrintObjects and their volumes. + // Regions with zero references could and should be reused. + for (PrintRegion *region : m_regions) + region->m_refcnt = 0; + for (PrintObject *print_object : m_objects) { + int idx_region = 0; + for (const auto &volumes : print_object->region_volumes) { + if (! volumes.empty()) + ++ m_regions[idx_region]; + ++ idx_region; + } + } + + // All regions now have distinct settings. + // Check whether applying the new region config defaults we'd get different regions. + for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { + PrintRegion ®ion = *m_regions[region_id]; + PrintRegionConfig this_region_config; + bool this_region_config_set = false; + for (PrintObject *print_object : m_objects) { + if (region_id < print_object->region_volumes.size()) { + for (int volume_id : print_object->region_volumes[region_id]) { + const ModelVolume &volume = *print_object->model_object()->volumes[volume_id]; + if (this_region_config_set) { + // If the new config for this volume differs from the other + // volume configs currently associated to this region, it means + // the region subdivision does not make sense anymore. + if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume))) + // Regions were split. Reset this print_object. + goto print_object_end; + } else { + this_region_config = region_config_from_model_volume(m_default_region_config, volume); + for (size_t i = 0; i < region_id; ++ i) + if (m_regions[i]->config().equals(this_region_config)) + // Regions were merged. Reset this print_object. + goto print_object_end; + this_region_config_set = true; + } + } } continue; + print_object_end: + 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) { + if (! volumes.empty()) + -- m_regions[ireg]; + ++ ireg; + } + print_object->region_volumes.clear(); + } + if (this_region_config_set) { + t_config_option_keys diff = region.config().diff(this_region_config); + if (! diff.empty()) { + 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); + } } - // Complex case, try to merge the two lists. - // Sort the old lexicographically by their trafos. - std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); - // Merge the old / new lists. - - } - if (m_objects != print_objects_new) { - m_cancel_callback(); - m_objects = print_objects_new; } - // Synchronize materials. - -#if 0 - { - m_model = model; - for (const ModelObject *model_object : m_model.objects) { - PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); - m_objects.emplace_back(object); - size_t volume_id = 0; - for (const ModelVolume *volume : model_object->volumes) { + // Possibly add new regions for the newly added or resetted PrintObjects. + for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) { + PrintObject &print_object0 = *m_objects[idx_print_object]; + const ModelObject &model_object = *print_object0.model_object(); + std::vector map_volume_to_region(model_object.volumes.size(), -1); + for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) { + PrintObject &print_object = *m_objects[i]; + unsigned int volume_id = 0; + for (const ModelVolume *volume : model_object.volumes) { if (! volume->is_model_part() && ! volume->is_modifier()) continue; - // Get the config applied to this volume. - PrintRegionConfig config = this->_region_config_from_model_volume(*volume); - // Find an existing print region with the same config. - size_t region_id = size_t(-1); - for (size_t i = 0; i < m_regions.size(); ++ i) - if (config.equals(m_regions[i]->config())) { - region_id = i; - break; + int region_id = -1; + if (&print_object == &print_object0) { + // Get the config applied to this volume. + PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume); + // Find an existing print region with the same config. + for (int i = 0; i < (int)m_regions.size(); ++ i) + if (config.equals(m_regions[i]->config())) { + region_id = i; + break; + } + // If no region exists with the same config, create a new one. + if (region_id == size_t(-1)) { + for (region_id = 0; region_id < m_regions.size(); ++ region_id) + if (m_regions[region_id]->m_refcnt == 0) { + // An empty slot was found. + m_regions[region_id]->set_config(std::move(config)); + break; + } + if (region_id == m_regions.size()) + this->add_region(config); } - // If no region exists with the same config, create a new one. - if (region_id == size_t(-1)) { - region_id = m_regions.size(); - this->add_region(config); - } + map_volume_to_region[volume_id] = region_id; + } else + region_id = map_volume_to_region[volume_id]; // Assign volume to a region. - object->add_region_volume(region_id, volume_id); + if (volume_id >= print_object.region_volumes.size()) + print_object.add_region_volume(region_id, volume_id); ++ volume_id; } - // Apply config to print object. - object->config_apply(this->default_object_config()); - { - //normalize_and_apply_config(object->config(), model_object->config); - DynamicPrintConfig src_normalized(model_object->config); - src_normalized.normalize(); - object->config_apply(src_normalized, true); - } } - } else { - // Synchronize m_model.objects with model.objects } -#endif this->update_object_placeholders(); } @@ -1284,16 +1390,6 @@ Flow Print::skirt_flow() const ); } -PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume) -{ - PrintRegionConfig config = this->default_region_config(); - normalize_and_apply_config(config, volume.get_object()->config); - normalize_and_apply_config(config, volume.config); - if (! volume.material_id().empty()) - normalize_and_apply_config(config, volume.material()->config); - return config; -} - bool Print::has_support_material() const { for (const PrintObject *object : m_objects) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d539053b3..f4fd2235b 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -139,19 +139,25 @@ public: // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; // Average diameter of nozzles participating on extruding this region. - coordf_t bridging_height_avg(const PrintConfig &print_config) const; + coordf_t bridging_height_avg(const PrintConfig &print_config) const; // Methods modifying the PrintRegion's state: public: Print* print() { return m_print; } - void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } + void set_config(const PrintRegionConfig &config) { m_config = config; } + void set_config(PrintRegionConfig &&config) { m_config = std::move(config); } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) + { this->m_config.apply_only(other, keys, ignore_nonexistent); } + +protected: + size_t m_refcnt; private: Print *m_print; PrintRegionConfig m_config; - PrintRegion(Print* print) : m_print(print) {} - PrintRegion(Print* print, const PrintRegionConfig &config) : m_print(print), m_config(config) {} + PrintRegion(Print* print) : m_refcnt(0), m_print(print) {} + PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {} ~PrintRegion() {} }; @@ -196,12 +202,13 @@ public: const SupportLayerPtrs& support_layers() const { return m_support_layers; } const Transform3d& trafo() const { return m_trafo; } - const Points& copies() const { return m_copies; } - bool add_copy(const Vec2d &point); - bool delete_last_copy(); - bool delete_all_copies() { return this->set_copies(Points()); } - bool set_copies(const Points &points); - bool reload_model_instances(); + const Points& copies() const { return m_copies; } + bool add_copy(const Vec2d &point); + bool delete_last_copy(); + bool delete_all_copies() { return this->set_copies(Points()); } + bool set_copies(const Points &points); + bool reload_model_instances(); + // since the object is aligned to origin, bounding box coincides with size BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } @@ -486,7 +493,6 @@ private: void update_object_placeholders(); bool invalidate_state_by_config_options(const std::vector &opt_keys); - PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); // If the background processing stop was requested, throw CanceledException. // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.