From 54976e29bbecae75d6b5bd65454d9b7d65e9d461 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 15:34:13 +0200 Subject: [PATCH 1/7] New class ModelConfig wrapping DynamicPrintConfig and a timestamp to help with detecting "not changed" event when taking Undo/Redo snapshot or synchronizing with the back-end. Converted layer height profile and supports / seam painted areas to the same timestamp controlled structure. --- src/libslic3r/Format/3mf.cpp | 20 +++-- src/libslic3r/Format/AMF.cpp | 10 ++- src/libslic3r/Model.cpp | 33 +++---- src/libslic3r/Model.hpp | 93 +++++++++++-------- src/libslic3r/ObjectID.cpp | 2 + src/libslic3r/ObjectID.hpp | 40 ++++++++- src/libslic3r/Print.cpp | 22 ++--- src/libslic3r/PrintConfig.cpp | 2 + src/libslic3r/PrintConfig.hpp | 90 +++++++++++++++++++ src/libslic3r/PrintObject.cpp | 8 +- src/libslic3r/SLAPrint.cpp | 4 +- src/libslic3r/Slicing.cpp | 13 +-- src/libslic3r/Slicing.hpp | 5 +- src/slic3r/GUI/ConfigManipulation.cpp | 1 + src/slic3r/GUI/ConfigManipulation.hpp | 8 +- src/slic3r/GUI/GLCanvas3D.cpp | 6 +- src/slic3r/GUI/GUI_ObjectLayers.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.cpp | 95 ++++++++++---------- src/slic3r/GUI/GUI_ObjectList.hpp | 13 +-- src/slic3r/GUI/GUI_ObjectSettings.cpp | 12 ++- src/slic3r/GUI/GUI_ObjectSettings.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 16 ++-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 14 +-- src/slic3r/GUI/OptionsGroup.cpp | 23 +++-- src/slic3r/GUI/OptionsGroup.hpp | 32 +++++-- src/slic3r/GUI/Selection.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 8 +- src/slic3r/Utils/UndoRedo.cpp | 2 +- 28 files changed, 366 insertions(+), 215 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 46a6c02af..436a2b29f 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -683,23 +683,23 @@ namespace Slic3r { // m_layer_heights_profiles are indexed by a 1 based model object index. IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1); if (obj_layer_heights_profile != m_layer_heights_profiles.end()) - model_object->layer_height_profile = obj_layer_heights_profile->second; + model_object->layer_height_profile.set(std::move(obj_layer_heights_profile->second)); // m_layer_config_ranges are indexed by a 1 based model object index. IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1); if (obj_layer_config_ranges != m_layer_config_ranges.end()) - model_object->layer_config_ranges = obj_layer_config_ranges->second; + model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second); // m_sla_support_points are indexed by a 1 based model object index. IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1); if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) { - model_object->sla_support_points = obj_sla_support_points->second; + model_object->sla_support_points = std::move(obj_sla_support_points->second); model_object->sla_points_status = sla::PointsStatus::UserModified; } IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1); if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) { - model_object->sla_drain_holes = obj_drain_holes->second; + model_object->sla_drain_holes = std::move(obj_drain_holes->second); } IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); @@ -934,7 +934,7 @@ namespace Slic3r { double max_z = range_tree.get(".max_z"); // get Z range information - DynamicPrintConfig& config = config_ranges[{ min_z, max_z }]; + DynamicPrintConfig config; for (const auto& option : range_tree) { @@ -945,10 +945,12 @@ namespace Slic3r { config.set_deserialize(opt_key, value); } + + config_ranges[{ min_z, max_z }].assign_config(std::move(config)); } if (!config_ranges.empty()) - m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, config_ranges)); + m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, std::move(config_ranges))); } } } @@ -2407,7 +2409,7 @@ namespace Slic3r { triangles_count += (int)its.indices.size(); volume_it->second.last_triangle_id = triangles_count - 1; - for (size_t i = 0; i < its.indices.size(); ++ i) + for (int i = 0; i < int(its.indices.size()); ++ i) { stream << " <" << TRIANGLE_TAG << " "; for (int j = 0; j < 3; ++j) @@ -2472,7 +2474,7 @@ namespace Slic3r { for (const ModelObject* object : model.objects) { ++count; - const std::vector &layer_height_profile = object->layer_height_profile; + const std::vector& layer_height_profile = object->layer_height_profile.get(); if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0)) { sprintf(buffer, "object_id=%d|", count); @@ -2527,7 +2529,7 @@ namespace Slic3r { range_tree.put(".max_z", range.first.second); // store range configuration - const DynamicPrintConfig& config = range.second; + const ModelConfig& config = range.second; for (const std::string& opt_key : config.keys()) { pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key)); diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 1a706afa9..a2117d63b 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -688,7 +688,7 @@ void AMFParserContext::endElement(const char * /* name */) else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) { const char *opt_key = m_value[0].c_str() + 7; if (print_config_def.options.find(opt_key) != print_config_def.options.end()) { - DynamicPrintConfig *config = nullptr; + ModelConfig *config = nullptr; if (m_path.size() == 3) { if (m_path[1] == NODE_TYPE_MATERIAL && m_material) config = &m_material->config; @@ -706,15 +706,17 @@ void AMFParserContext::endElement(const char * /* name */) } else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) { // Parse object's layer height profile, a semicolon separated list of floats. char *p = m_value[1].data(); + std::vector data; for (;;) { char *end = strchr(p, ';'); if (end != nullptr) *end = 0; - m_object->layer_height_profile.push_back(float(atof(p))); + data.emplace_back(float(atof(p))); if (end == nullptr) break; p = end + 1; } + m_object->layer_height_profile.set(std::move(data)); } else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) { // Parse object's layer height profile, a semicolon separated list of floats. @@ -1095,7 +1097,7 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, stream << " " << object->config.opt_serialize(key) << "\n"; if (!object->name.empty()) stream << " " << xml_escape(object->name) << "\n"; - const std::vector &layer_height_profile = object->layer_height_profile; + const std::vector &layer_height_profile = object->layer_height_profile.get(); if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) { // Store the layer height profile as a single semicolon separated list. stream << " "; @@ -1112,7 +1114,7 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, // Store the layer config range as a single semicolon separated list. stream << " \n"; size_t layer_counter = 0; - for (auto range : config_ranges) { + for (const auto &range : config_ranges) { stream << " \n"; stream << " "; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3539cc0fa..a4541eeec 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1050,7 +1050,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial ModelVolume* vol = new_object->add_volume(mesh); vol->name = volume->name; // Don't copy the config's ID. - static_cast(vol->config) = static_cast(volume->config); + vol->config.assign_config(volume->config); assert(vol->config.id().valid()); assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); @@ -1193,7 +1193,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; // Don't copy the config's ID. - static_cast(vol->config) = static_cast(volume->config); + vol->config.assign_config(volume->config); assert(vol->config.id().valid()); assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); @@ -1202,8 +1202,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; // Don't copy the config's ID. - static_cast(vol->config) = static_cast(volume->config); - assert(vol->config.id().valid()); + vol->config.assign_config(volume->config); + assert(vol->config.id().valid()); assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); @@ -1280,7 +1280,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) ModelObject* new_object = m_model->add_object(); new_object->name = this->name; // Don't copy the config's ID. - static_cast(new_object->config) = static_cast(this->config); + new_object->config.assign_config(this->config); assert(new_object->config.id().valid()); assert(new_object->config.id() != this->config.id()); new_object->instances.reserve(this->instances.size()); @@ -1867,7 +1867,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const return ret; } - indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const { TriangleSelector selector(mv.mesh()); @@ -1876,29 +1875,23 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce return out; } - - bool FacetsAnnotation::set(const TriangleSelector& selector) { std::map> sel_map = selector.serialize(); if (sel_map != m_data) { m_data = sel_map; - update_timestamp(); + this->touch(); return true; } return false; } - - void FacetsAnnotation::clear() { m_data.clear(); - update_timestamp(); + this->reset_timestamp(); } - - // Following function takes data from a triangle and encodes it as string // of hexadecimal numbers (one digit per triangle). Used for 3MF export, // changing it may break backwards compatibility !!!!! @@ -1926,8 +1919,6 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const return out; } - - // Recover triangle splitting & state from string of hexadecimal values previously // generated by get_triangle_as_string. Used to load from 3MF. void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str) @@ -1951,12 +1942,8 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri code.insert(code.end(), bool(dec & (1 << i))); } } - - } - - // 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) @@ -2024,7 +2011,7 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); assert(mo.volumes.size() == mo_new.volumes.size()); for (size_t i=0; im_supported_facets.is_same_as(mo.volumes[i]->m_supported_facets)) + if (! mo_new.volumes[i]->m_supported_facets.timestamp_matches(mo.volumes[i]->m_supported_facets)) return true; } return false; @@ -2034,7 +2021,7 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); assert(mo.volumes.size() == mo_new.volumes.size()); for (size_t i=0; im_seam_facets.is_same_as(mo.volumes[i]->m_seam_facets)) + if (! mo_new.volumes[i]->m_seam_facets.timestamp_matches(mo.volumes[i]->m_seam_facets)) return true; } return false; @@ -2050,7 +2037,7 @@ extern bool model_has_multi_part_objects(const Model &model) extern bool model_has_advanced_features(const Model &model) { - auto config_is_advanced = [](const DynamicPrintConfig &config) { + auto config_is_advanced = [](const ModelConfig &config) { return ! (config.empty() || (config.size() == 1 && config.cbegin()->first == "extruder")); }; for (const ModelObject *model_object : model.objects) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c6b7c6030..003c4ed0f 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -18,7 +18,6 @@ #include #include #include -#include namespace cereal { class BinaryInputArchive; @@ -45,7 +44,7 @@ namespace UndoRedo { class StackImpl; } -class ModelConfig : public ObjectBase, public DynamicPrintConfig +class ModelConfigObject : public ObjectBase, public ModelConfig { private: friend class cereal::access; @@ -56,21 +55,25 @@ private: // Constructors to be only called by derived classes. // Default constructor to assign a unique ID. - explicit ModelConfig() {} + explicit ModelConfigObject() {} // Constructor with ignored int parameter to assign an invalid ID, to be replaced // by an existing ID copied from elsewhere. - explicit ModelConfig(int) : ObjectBase(-1) {} + explicit ModelConfigObject(int) : ObjectBase(-1) {} // Copy constructor copies the ID. - explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); } + explicit ModelConfigObject(const ModelConfigObject &cfg) : ObjectBase(-1), ModelConfig(cfg) { this->copy_id(cfg); } // Move constructor copies the ID. - explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); } + explicit ModelConfigObject(ModelConfigObject &&cfg) : ObjectBase(-1), ModelConfig(std::move(cfg)) { this->copy_id(cfg); } - ModelConfig& operator=(const ModelConfig &rhs) = default; - ModelConfig& operator=(ModelConfig &&rhs) = default; + Timestamp timestamp() const throw() override { return this->ModelConfig::timestamp(); } + bool object_id_and_timestamp_match(const ModelConfigObject &rhs) const throw() { return this->id() == rhs.id() && this->timestamp() == rhs.timestamp(); } - template void serialize(Archive &ar) { - ar(cereal::base_class(this)); - } + // called by ModelObject::assign_copy() + ModelConfigObject& operator=(const ModelConfigObject &rhs) = default; + ModelConfigObject& operator=(ModelConfigObject &&rhs) = default; + + template void serialize(Archive &ar) { + ar(cereal::base_class(this)); + } }; namespace Internal { @@ -136,7 +139,7 @@ public: // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. t_model_material_attributes attributes; // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. - ModelConfig config; + ModelConfigObject config; Model* get_model() const { return m_model; } void apply(const t_model_material_attributes &attributes) @@ -162,7 +165,7 @@ private: ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } template void serialize(Archive &ar) { assert(this->id().invalid()); assert(this->config.id().invalid()); - Internal::StaticSerializationWrapper config_wrapper(config); + Internal::StaticSerializationWrapper config_wrapper(config); ar(attributes, config_wrapper); // assert(this->id().valid()); assert(this->config.id().valid()); } @@ -173,6 +176,23 @@ private: ModelMaterial& operator=(ModelMaterial &&rhs) = delete; }; +class LayerHeightProfile final : public ObjectWithTimestamp { +public: + std::vector get() const throw() { return m_data; } + bool empty() const throw() { return m_data.empty(); } + void set(const std::vector &data) { if (m_data != data) { m_data = data; this->touch(); } } + void set(std::vector &&data) { if (m_data != data) { m_data = std::move(data); this->touch(); } } + void clear() { m_data.clear(); this->touch(); } + + template void serialize(Archive &ar) + { + ar(cereal::base_class(this), m_data); + } + +private: + std::vector m_data; +}; + // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), // and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. // Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, @@ -189,12 +209,12 @@ public: // ModelVolumes are owned by this ModelObject. ModelVolumePtrs volumes; // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. - ModelConfig config; + ModelConfigObject config; // Variation of a layer thickness for spans of Z coordinates + optional parameter overrides. t_layer_config_ranges layer_config_ranges; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. // The pairs of are packed into a 1D array. - std::vector layer_height_profile; + LayerHeightProfile layer_height_profile; // Whether or not this object is printable bool printable; @@ -381,8 +401,10 @@ private: } template void serialize(Archive &ar) { ar(cereal::base_class(this)); - Internal::StaticSerializationWrapper config_wrapper(config); - ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, + Internal::StaticSerializationWrapper config_wrapper(config); + Internal::StaticSerializationWrapper layer_heigth_profile_wrapper(layer_height_profile); + ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, + sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } }; @@ -403,34 +425,27 @@ enum class EnforcerBlockerType : int8_t { BLOCKER = 2 }; -class FacetsAnnotation { +class FacetsAnnotation final : public ObjectWithTimestamp { public: - using ClockType = std::chrono::steady_clock; - - const std::map>& get_data() const { return m_data; } + void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) this->m_data = rhs.m_data; } + void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) this->m_data = rhs.m_data; } + const std::map>& get_data() const throw() { return m_data; } bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; void clear(); std::string get_triangle_as_string(int i) const; void set_triangle_from_string(int triangle_id, const std::string& str); - ClockType::time_point get_timestamp() const { return timestamp; } - bool is_same_as(const FacetsAnnotation& other) const { - return timestamp == other.get_timestamp(); - } +private: + friend class cereal::access; + friend class UndoRedo::StackImpl; template void serialize(Archive &ar) { - ar(m_data); + ar(cereal::base_class(this), m_data); } -private: std::map> m_data; - - ClockType::time_point timestamp; - void update_timestamp() { - timestamp = ClockType::now(); - } }; // An object STL, or a modifier volume, over which a different set of parameters shall be applied. @@ -465,7 +480,7 @@ public: void reset_mesh() { m_mesh = std::make_shared(); } // Configuration parameters specific to an object model geometry or a modifier volume, // overriding the global Slic3r settings and the ModelObject settings. - ModelConfig config; + ModelConfigObject config; // List of mesh facets to be supported/unsupported. FacetsAnnotation m_supported_facets; @@ -634,8 +649,9 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, - m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::load_by_value(ar, m_supported_facets); + cereal::load_by_value(ar, m_seam_facets); cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { @@ -648,8 +664,9 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, - m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::save_by_value(ar, m_supported_facets); + cereal::save_by_value(ar, m_seam_facets); cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); @@ -935,7 +952,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2); namespace cereal { template struct specialize {}; - template struct specialize {}; + template struct specialize {}; } #endif /* slic3r_Model_hpp_ */ diff --git a/src/libslic3r/ObjectID.cpp b/src/libslic3r/ObjectID.cpp index b188d84c0..7188f05fd 100644 --- a/src/libslic3r/ObjectID.cpp +++ b/src/libslic3r/ObjectID.cpp @@ -17,6 +17,8 @@ ObjectID wipe_tower_instance_id() return mine.id(); } +size_t ObjectWithTimestamp::s_last_timestamp = 1; + } // namespace Slic3r // CEREAL_REGISTER_TYPE(Slic3r::ObjectBase) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index fd5cc80ae..bd2f6b86e 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -49,12 +49,14 @@ private: class ObjectBase { public: + using Timestamp = uint64_t; + ObjectID id() const { return m_id; } // Return an optional timestamp of this object. // If the timestamp returned is non-zero, then the serialization framework will // only save this object on the Undo/Redo stack if the timestamp is different // from the timestmap of the object at the top of the Undo / Redo stack. - virtual uint64_t timestamp() const { return 0; } + virtual Timestamp timestamp() const { return 0; } protected: // Constructors to be only called by derived classes. @@ -91,6 +93,42 @@ private: template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } }; +class ObjectWithTimestamp : public ObjectBase +{ +protected: + // Constructors to be only called by derived classes. + // Default constructor to assign a new timestamp unique to this object's history. + ObjectWithTimestamp() = default; + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + ObjectWithTimestamp(int) : ObjectBase(-1) {} + // The class tree will have virtual tables and type information. + virtual ~ObjectWithTimestamp() = default; + + // Resetting timestamp to 1 indicates the object is in its initial (cleared) state. + // To be called by the derived class's clear() method. + void reset_timestamp() { m_timestamp = 1; } + +public: + // Return an optional timestamp of this object. + // If the timestamp returned is non-zero, then the serialization framework will + // only save this object on the Undo/Redo stack if the timestamp is different + // from the timestmap of the object at the top of the Undo / Redo stack. + Timestamp timestamp() const throw() override { return m_timestamp; } + bool timestamp_matches(const ObjectWithTimestamp &rhs) const throw() { return m_timestamp == rhs.m_timestamp; } + bool object_id_and_timestamp_match(const ObjectWithTimestamp &rhs) const throw() { return this->id() == rhs.id() && m_timestamp == rhs.m_timestamp; } + void touch() { m_timestamp = ++ s_last_timestamp; } + +private: + // The first timestamp is non-zero, as zero timestamp means the timestamp is not reliable. + Timestamp m_timestamp { 1 }; + static Timestamp s_last_timestamp; + + friend class cereal::access; + friend class Slic3r::UndoRedo::StackImpl; + template void serialize(Archive &ar) { ar(m_timestamp); } +}; + // Unique object / instance ID for the wipe tower. extern ObjectID wipe_tower_object_id(); extern ObjectID wipe_tower_instance_id(); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a360d840f..fb276a7c2 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -403,9 +403,11 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, assert(mv_src.id() == mv_dst.id()); // Copy the ModelVolume data. mv_dst.name = mv_src.name; - static_cast(mv_dst.config) = static_cast(mv_src.config); - mv_dst.m_supported_facets = mv_src.m_supported_facets; - mv_dst.m_seam_facets = mv_src.m_seam_facets; + mv_dst.config.assign_config(mv_src.config); + if (! mv_dst.m_supported_facets.timestamp_matches(mv_src.m_supported_facets)) + mv_dst.m_supported_facets = mv_src.m_supported_facets; + if (! mv_dst.m_seam_facets.timestamp_matches(mv_src.m_seam_facets)) + mv_dst.m_seam_facets = mv_src.m_seam_facets; //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -644,7 +646,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ m_ranges.reserve(in.size()); // Input ranges are sorted lexicographically. First range trims the other ranges. coordf_t last_z = 0; - for (const std::pair &range : in) + for (const std::pair &range : in) if (range.first.second > last_z) { coordf_t min_z = std::max(range.first.first, 0.); if (min_z > last_z + EPSILON) { @@ -652,7 +654,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ last_z = min_z; } if (range.first.second > last_z + EPSILON) { - const DynamicPrintConfig* cfg = &range.second; + const DynamicPrintConfig *cfg = &range.second.get(); m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg); last_z = range.first.second; } @@ -845,8 +847,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER); bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); if (model_parts_differ || modifiers_differ || - model_object.origin_translation != model_object_new.origin_translation || - model_object.layer_height_profile != model_object_new.layer_height_profile || + model_object.origin_translation != model_object_new.origin_translation || + ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) || ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) { // 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())); @@ -874,9 +876,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } if (! model_parts_differ && ! modifiers_differ) { // Synchronize Object's config. - bool object_config_changed = model_object.config != model_object_new.config; + bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config); if (object_config_changed) - static_cast(model_object.config) = static_cast(model_object_new.config); + model_object.config.assign_config(model_object_new.config); if (! object_diff.empty() || object_config_changed || num_extruders_changed) { PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); @@ -1577,7 +1579,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const ModelVolume *volume = model_object->volumes[volume_id]; //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder")) - volume->config.opt("extruder", true)->value = int(volume_id + 1); + volume->config.set("extruder", int(volume_id + 1)); } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 72393a3f5..a8eb68521 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3718,6 +3718,8 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } } +uint64_t ModelConfig::s_last_timestamp = 1; + static Points to_points(const std::vector &dpts) { Points pts; pts.reserve(dpts.size()); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index fa7edd10e..bb4428ab4 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1369,6 +1369,96 @@ Points get_bed_shape(const DynamicPrintConfig &cfg); Points get_bed_shape(const PrintConfig &cfg); Points get_bed_shape(const SLAPrinterConfig &cfg); +// ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp. +// Each change of ModelConfig is tracked by assigning a new timestamp from a global counter. +// The counter is used for faster synchronization of the background slicing thread +// with the front end by skipping synchronization of equal config dictionaries. +// The global counter is also used for avoiding unnecessary serialization of config +// dictionaries when taking an Undo snapshot. +// +// The global counter is NOT thread safe, therefore it is recommended to use ModelConfig from +// the main thread only. +// +// As there is a global counter and it is being increased with each change to any ModelConfig, +// if two ModelConfig dictionaries differ, they should differ with their timestamp as well. +// Therefore copying the ModelConfig including its timestamp is safe as there is no harm +// in having multiple ModelConfig with equal timestamps as long as their dictionaries are equal. +// +// The timestamp is used by the Undo/Redo stack. As zero timestamp means invalid timestamp +// to the Undo/Redo stack (zero timestamp means the Undo/Redo stack needs to serialize and +// compare serialized data for differences), zero timestamp shall never be used. +// Timestamp==1 shall only be used for empty dictionaries. +class ModelConfig +{ +public: + void clear() { m_data.clear(); m_timestamp = 1; } + + // Modification of the ModelConfig is not thread safe due to the global timestamp counter! + // Don't call modification methods from the back-end! + void assign_config(const ModelConfig &rhs) { + if (m_timestamp != rhs.m_timestamp) { + m_data = rhs.m_data; + m_timestamp = rhs.m_timestamp; + } + } + void assign_config(ModelConfig &&rhs) { + if (m_timestamp != rhs.m_timestamp) { + m_data = std::move(rhs.m_data); + m_timestamp = rhs.m_timestamp; + rhs.clear(); + } + } + // Assign methods don't assign if src==dst to not having to bump the timestamp in case they are equal. + void assign_config(const DynamicPrintConfig &rhs) { if (m_data != rhs) { m_data = rhs; this->touch(); } } + void assign_config(DynamicPrintConfig &&rhs) { if (m_data != rhs) { m_data = std::move(rhs); this->touch(); } } + void apply(const ModelConfig &other, bool ignore_nonexistent = false) { this->apply(other.get(), ignore_nonexistent); } + void apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_data.apply_only(other, other.keys(), ignore_nonexistent); this->touch(); } + void apply_only(const ModelConfig &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->apply_only(other.get(), keys, ignore_nonexistent); } + void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_data.apply_only(other, keys, ignore_nonexistent); this->touch(); } + bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; } + template + void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } + void set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false) + { m_data.set_deserialize(opt_key, str, append); this->touch(); } + bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } + + // Getters are thread safe. + // The following implicit conversion breaks the Cereal serialization. +// operator const DynamicPrintConfig&() const throw() { return this->get(); } + const DynamicPrintConfig& get() const throw() { return m_data; } + bool empty() const throw() { return m_data.empty(); } + size_t size() const throw() { return m_data.size(); } + auto cbegin() const { return m_data.cbegin(); } + auto cend() const { return m_data.cend(); } + t_config_option_keys keys() const { return m_data.keys(); } + bool has(const t_config_option_key &opt_key) const { return m_data.has(opt_key); } + const ConfigOption* option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); } + int opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); } + int extruder() const { return opt_int("extruder"); } + double opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); } + std::string opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); } + + // Return an optional timestamp of this object. + // If the timestamp returned is non-zero, then the serialization framework will + // only save this object on the Undo/Redo stack if the timestamp is different + // from the timestmap of the object at the top of the Undo / Redo stack. + virtual uint64_t timestamp() const throw() { return m_timestamp; } + bool timestamp_matches(const ModelConfig &rhs) const throw() { return m_timestamp == rhs.m_timestamp; } + // Not thread safe! Should not be called from other than the main thread! + void touch() { m_timestamp = ++ s_last_timestamp; } + +private: + friend class cereal::access; + template void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); } + + uint64_t m_timestamp { 1 }; + DynamicPrintConfig m_data; + + static uint64_t s_last_timestamp; +}; + +template void normalize_and_apply_config(CONFIG& dst, const ModelConfig& src) { normalize_and_apply_config(dst, src.get()); } + } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4b83062a0..c0425ada4 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1592,13 +1592,13 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full print_config, region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders), object_extruders); - for (const std::pair &range_and_config : model_object.layer_config_ranges) + for (const std::pair &range_and_config : model_object.layer_config_ranges) if (range_and_config.second.has("perimeter_extruder") || range_and_config.second.has("infill_extruder") || range_and_config.second.has("solid_infill_extruder")) PrintRegion::collect_object_printing_extruders( print_config, - region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders), + region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, num_extruders), object_extruders); } sort_remove_duplicates(object_extruders); @@ -1626,7 +1626,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c if (layer_height_profile.empty()) { // use the constructor because the assignement is crashing on ASAN OsX - layer_height_profile = std::vector(model_object.layer_height_profile); + layer_height_profile = std::vector(model_object.layer_height_profile.get()); // layer_height_profile = model_object.layer_height_profile; updated = true; } @@ -2872,7 +2872,7 @@ void PrintObject::project_and_append_custom_facets( // Now append the collected polygons to respective layers. for (auto& trg : projections_of_triangles) { - int layer_id = trg.first_layer_id; + int layer_id = int(trg.first_layer_id); for (const LightPolygon& poly : trg.polygons) { if (layer_id >= int(expolys.size())) break; // part of triangle could be projected above top layer diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 07ec38016..30d1df6c1 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -395,9 +395,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con model_object.assign_copy(model_object_new); } else { // Synchronize Object's config. - bool object_config_changed = model_object.config != model_object_new.config; + bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config); if (object_config_changed) - static_cast(model_object.config) = static_cast(model_object_new.config); + model_object.config.assign_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); diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 82d2d1989..16068dde4 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -170,24 +170,15 @@ SlicingParameters SlicingParameters::create_from_config( return params; } -std::vector> layer_height_ranges(const t_layer_config_ranges &config_ranges) -{ - std::vector> out; - out.reserve(config_ranges.size()); - for (const auto &kvp : config_ranges) - out.emplace_back(kvp.first, kvp.second.option("layer_height")->getFloat()); - return out; -} - // Convert layer_config_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for // in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation. std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, - const t_layer_config_ranges &layer_config_ranges) // #ys_FIXME_experiment + const t_layer_config_ranges &layer_config_ranges) { // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed. std::vector> ranges_non_overlapping; - ranges_non_overlapping.reserve(layer_config_ranges.size() * 4); // #ys_FIXME_experiment + ranges_non_overlapping.reserve(layer_config_ranges.size() * 4); if (slicing_params.first_object_layer_height_fixed()) ranges_non_overlapping.push_back(std::pair( t_layer_height_range(0., slicing_params.first_object_layer_height), diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 2fd609b2c..e151b208f 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -17,6 +17,7 @@ namespace Slic3r class PrintConfig; class PrintObjectConfig; +class ModelConfig; class ModelObject; class DynamicPrintConfig; @@ -128,9 +129,7 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters } typedef std::pair t_layer_height_range; -typedef std::map t_layer_config_ranges; - -extern std::vector> layer_height_ranges(const t_layer_config_ranges &config_ranges); +typedef std::map t_layer_config_ranges; extern std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 5836b8a2c..5fdfac8b4 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -2,6 +2,7 @@ #include "ConfigManipulation.hpp" #include "I18N.hpp" #include "GUI_App.hpp" +#include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" #include diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index 7344f758b..ecf849447 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -10,9 +10,11 @@ #include "libslic3r/PrintConfig.hpp" #include "Field.hpp" -//#include namespace Slic3r { + +class ModelConfig; + namespace GUI { class ConfigManipulation @@ -24,13 +26,13 @@ class ConfigManipulation std::function get_field = nullptr; // callback to propagation of changed value, if needed std::function cb_value_change = nullptr; - DynamicPrintConfig* local_config = nullptr; + ModelConfig* local_config = nullptr; public: ConfigManipulation(std::function load_config, std::function get_field, std::function cb_value_change, - DynamicPrintConfig* local_config = nullptr) : + ModelConfig* local_config = nullptr) : load_config(load_config), get_field(get_field), cb_value_change(cb_value_change), diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e0c8c4c5b..3e5712b73 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -511,7 +511,7 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas { this->update_slicing_parameters(); m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, quality_factor); - const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -520,7 +520,7 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, { this->update_slicing_parameters(); m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params); - const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -560,7 +560,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) if (last_object_id >= 0) { if (m_layer_height_profile_modified) { wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit"))); - const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } } diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 90a725fbf..e8fab2a79 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -153,7 +153,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus void ObjectLayers::create_layers_list() { - for (const auto layer : m_object->layer_config_ranges) + for (const auto &layer : m_object->layer_config_ranges) { const t_layer_height_range& range = layer.first; auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4973abca5..1925401b8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -468,7 +468,7 @@ int ObjectList::get_selected_obj_idx() const return -1; } -DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) const +ModelConfig& ObjectList::get_item_config(const wxDataViewItem& item) const { assert(item); const ItemType type = m_objects_model->GetItemType(item); @@ -492,10 +492,10 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder) auto object = (*m_objects)[i]; wxString extruder; if (!object->config.has("extruder") || - size_t(object->config.option("extruder")->value) > max_extruder) + size_t(object->config.extruder()) > max_extruder) extruder = _(L("default")); else - extruder = wxString::Format("%d", object->config.option("extruder")->value); + extruder = wxString::Format("%d", object->config.extruder()); m_objects_model->SetExtruder(extruder, item); @@ -504,10 +504,10 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder) item = m_objects_model->GetItemByVolumeId(i, id); if (!item) continue; if (!object->volumes[id]->config.has("extruder") || - size_t(object->volumes[id]->config.option("extruder")->value) > max_extruder) + size_t(object->volumes[id]->config.extruder()) > max_extruder) extruder = _(L("default")); else - extruder = wxString::Format("%d", object->volumes[id]->config.option("extruder")->value); + extruder = wxString::Format("%d", object->volumes[id]->config.extruder()); m_objects_model->SetExtruder(extruder, item); } @@ -767,8 +767,7 @@ void ObjectList::copy_settings_to_clipboard() if (m_objects_model->GetItemType(item) & itSettings) item = m_objects_model->GetParent(item); - DynamicPrintConfig& config_cache = m_clipboard.get_config_cache(); - config_cache = get_item_config(item); + m_clipboard.get_config_cache() = get_item_config(item).get(); } void ObjectList::paste_settings_into_list() @@ -799,7 +798,7 @@ void ObjectList::paste_settings_into_list() } // Add settings item for object/sub-object and show them - show_settings(add_settings_item(item, m_config)); + show_settings(add_settings_item(item, &m_config->get())); } void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes) @@ -818,8 +817,8 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol { const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(), volume->get_mesh_errors_count()>0 , - volume->config.has("extruder") ? volume->config.option("extruder")->value : 0); - add_settings_item(vol_item, &volume->config); + volume->config.has("extruder") ? volume->config.extruder() : 0); + add_settings_item(vol_item, &volume->config.get()); items.Add(vol_item); } @@ -1480,7 +1479,7 @@ void ObjectList::get_settings_choice(const wxString& category_name) // Add settings item for object/sub-object and show them if (!(item_type & (itObject | itVolume | itLayer))) item = m_objects_model->GetTopParent(item); - show_settings(add_settings_item(item, m_config)); + show_settings(add_settings_item(item, &m_config->get())); } void ObjectList::get_freq_settings_choice(const wxString& bundle_name) @@ -1537,7 +1536,7 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) // Add settings item for object/sub-object and show them if (!(item_type & (itObject | itVolume | itLayer))) item = m_objects_model->GetTopParent(item); - show_settings(add_settings_item(item, m_config)); + show_settings(add_settings_item(item, &m_config->get())); } void ObjectList::show_settings(const wxDataViewItem settings_item) @@ -1821,9 +1820,8 @@ void ObjectList::append_menu_item_change_extruder(wxMenu* menu) int initial_extruder = -1; // negative value for multiple object/part selection if (sels.Count()==1) { - DynamicPrintConfig& config = get_item_config(sels[0]); - initial_extruder = !config.has("extruder") ? 0 : - config.option("extruder")->value; + const ModelConfig &config = get_item_config(sels[0]); + initial_extruder = config.has("extruder") ? config.extruder() : 0; } for (int i = 0; i <= extruders_cnt; i++) @@ -2320,9 +2318,7 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) take_snapshot(_(L("Delete Settings"))); - int extruder = -1; - if (m_config->has("extruder")) - extruder = m_config->option("extruder")->value; + int extruder = m_config->has("extruder") ? m_config->extruder() : -1; coordf_t layer_height = 0.0; if (is_layer_settings) @@ -2459,11 +2455,10 @@ void ObjectList::split() const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name), volume->is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART, volume->get_mesh_errors_count()>0, - volume->config.has("extruder") ? - volume->config.option("extruder")->value : 0, + volume->config.has("extruder") ? volume->config.extruder() : 0, false); // add settings to the part, if it has those - add_settings_item(vol_item, &volume->config); + add_settings_item(vol_item, &volume->config.get()); } model_object->input_file.clear(); @@ -2579,7 +2574,7 @@ void ObjectList::merge(bool to_multipart_object) Model* model = (*m_objects)[0]->get_model(); ModelObject* new_object = model->add_object(); new_object->name = _u8L("Merged"); - DynamicPrintConfig* config = &new_object->config; + ModelConfig &config = new_object->config; for (int obj_idx : obj_idxs) { @@ -2616,8 +2611,8 @@ void ObjectList::merge(bool to_multipart_object) } // merge settings - auto new_opt_keys = config->keys(); - const DynamicPrintConfig& from_config = object->config; + auto new_opt_keys = config.keys(); + const ModelConfig& from_config = object->config; auto opt_keys = from_config.keys(); for (auto& opt_key : opt_keys) { @@ -2628,7 +2623,7 @@ void ObjectList::merge(bool to_multipart_object) // get it from default config values option = DynamicPrintConfig::new_from_defaults_keys({ opt_key })->option(opt_key); } - config->set_key_value(opt_key, option->clone()); + config.set_key_value(opt_key, option->clone()); } } // save extruder value if it was set @@ -2695,7 +2690,7 @@ void ObjectList::layers_editing() // set some default value if (ranges.empty()) { take_snapshot(_(L("Add Layers"))); - ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx); + ranges[{ 0.0f, 2.0f }].assign_config(get_default_layer_config(obj_idx)); } // create layer root item @@ -3011,8 +3006,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) auto model_object = (*m_objects)[obj_idx]; const wxString& item_name = from_u8(model_object->name); const auto item = m_objects_model->Add(item_name, - !model_object->config.has("extruder") ? 0 : - model_object->config.option("extruder")->value, + model_object->config.has("extruder") ? model_object->config.extruder() : 0, get_mesh_errors_count(obj_idx) > 0); // add volumes to the object @@ -3022,10 +3016,9 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) from_u8(volume->name), volume->type(), volume->get_mesh_errors_count()>0, - !volume->config.has("extruder") ? 0 : - volume->config.option("extruder")->value, + volume->config.has("extruder") ? volume->config.extruder() : 0, false); - add_settings_item(vol_item, &volume->config); + add_settings_item(vol_item, &volume->config.get()); } Expand(item); } @@ -3045,7 +3038,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) m_objects_model->SetPrintableState(model_object->instances[0]->printable ? piPrintable : piUnprintable, obj_idx); // add settings to the object, if it has those - add_settings_item(item, &model_object->config); + add_settings_item(item, &model_object->config.get()); // Add layers if it has add_layer_root_item(item); @@ -3123,7 +3116,7 @@ void ObjectList::delete_from_model_and_list(const std::vector& it if ((*m_objects)[item->obj_idx]->volumes.size() == 1 && (*m_objects)[item->obj_idx]->config.has("extruder")) { - const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.option("extruder")->value); + const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.extruder()); m_objects_model->SetExtruder(extruder, m_objects_model->GetItemById(item->obj_idx)); } wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx); @@ -3289,7 +3282,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren const wxDataViewItem layers_item = GetSelection(); - t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; + auto& ranges = object(obj_idx)->layer_config_ranges; auto it_range = ranges.find(current_range); assert(it_range != ranges.end()); if (it_range == ranges.end()) @@ -3305,7 +3298,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren changed = true; const t_layer_height_range new_range = { current_range.second, current_range.second + 2. }; - ranges[new_range] = get_default_layer_config(obj_idx); + ranges[new_range].assign_config(get_default_layer_config(obj_idx)); add_layer_item(new_range, layers_item); } else if (const std::pair &next_range = it_next_range->first; current_range.second <= next_range.first) @@ -3342,7 +3335,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren add_layer_item(new_range, layers_item, layer_idx); new_range = { current_range.second, middle_layer_z }; - ranges[new_range] = get_default_layer_config(obj_idx); + ranges[new_range].assign_config(get_default_layer_config(obj_idx)); add_layer_item(new_range, layers_item, layer_idx); } } @@ -3353,7 +3346,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren changed = true; const t_layer_height_range new_range = { current_range.second, next_range.first }; - ranges[new_range] = get_default_layer_config(obj_idx); + ranges[new_range].assign_config(get_default_layer_config(obj_idx)); add_layer_item(new_range, layers_item, layer_idx); } } @@ -3379,7 +3372,7 @@ wxString ObjectList::can_add_new_range_after_current(const t_layer_height_range // This should not happen. return "ObjectList assert"; - t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; + auto& ranges = object(obj_idx)->layer_config_ranges; auto it_range = ranges.find(current_range); assert(it_range != ranges.end()); if (it_range == ranges.end()) @@ -3418,7 +3411,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range, const int obj_idx = m_objects_model->GetObjectIdByItem(layers_item); if (obj_idx < 0) return; - const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range]; + const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range].get(); if (!config.has("extruder")) return; @@ -3438,7 +3431,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t la if (obj_idx < 0) return false; - DynamicPrintConfig* config = &object(obj_idx)->layer_config_ranges[range]; + ModelConfig* config = &object(obj_idx)->layer_config_ranges[range]; if (fabs(layer_height - config->opt_float("layer_height")) < EPSILON) return false; @@ -3467,12 +3460,14 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay const ItemType sel_type = m_objects_model->GetItemType(GetSelection()); - t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; + auto& ranges = object(obj_idx)->layer_config_ranges; - const DynamicPrintConfig config = ranges[range]; + { + ModelConfig config = std::move(ranges[range]); + ranges.erase(range); + ranges[new_range] = std::move(config); + } - ranges.erase(range); - ranges[new_range] = config; changed_object(obj_idx); wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx)); @@ -4040,7 +4035,7 @@ void ObjectList::change_part_type() } else if (!settings_item && (new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) { - add_settings_item(item, &volume->config); + add_settings_item(item, &volume->config.get()); } } @@ -4065,14 +4060,14 @@ void ObjectList::update_and_show_object_settings_item() if (!item) return; const wxDataViewItem& obj_item = m_objects_model->IsSettingsItem(item) ? m_objects_model->GetParent(item) : item; - select_item(add_settings_item(obj_item, &get_item_config(obj_item))); + select_item(add_settings_item(obj_item, &get_item_config(obj_item).get())); } // Update settings item for item had it void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections) { const wxDataViewItem old_settings_item = m_objects_model->GetSettingsItem(item); - const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item)); + const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item).get()); if (!new_settings_item && old_settings_item) m_objects_model->Delete(old_settings_item); @@ -4489,19 +4484,19 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const for (const wxDataViewItem& item : sels) { - DynamicPrintConfig& config = get_item_config(item); + ModelConfig& config = get_item_config(item); if (config.has("extruder")) { if (extruder == 0) config.erase("extruder"); else - config.option("extruder")->value = extruder; + config.set("extruder", extruder); } else if (extruder > 0) config.set_key_value("extruder", new ConfigOptionInt(extruder)); const wxString extruder_str = extruder == 0 ? wxString (_(L("default"))) : - wxString::Format("%d", config.option("extruder")->value); + wxString::Format("%d", config.extruder()); auto const type = m_objects_model->GetItemType(item); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 9f7dcd247..df27fcbdb 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -24,6 +24,7 @@ class MenuWithSeparators; namespace Slic3r { class ConfigOptionsGroup; class DynamicPrintConfig; +class ModelConfig; class ModelObject; class ModelVolume; class TriangleMesh; @@ -39,9 +40,9 @@ typedef std::map< std::string, std::vector< std::pair typedef std::vector ModelVolumePtrs; -typedef double coordf_t; -typedef std::pair t_layer_height_range; -typedef std::map t_layer_config_ranges; +typedef double coordf_t; +typedef std::pair t_layer_height_range; +typedef std::map t_layer_config_ranges; namespace GUI { @@ -165,7 +166,7 @@ private: wxMenuItem* m_menu_item_split_instances { nullptr }; ObjectDataViewModel *m_objects_model{ nullptr }; - DynamicPrintConfig *m_config {nullptr}; + ModelConfig *m_config {nullptr}; std::vector *m_objects{ nullptr }; wxBitmapComboBox *m_extruder_editor { nullptr }; @@ -210,7 +211,7 @@ public: std::map CATEGORY_ICON; ObjectDataViewModel* GetModel() const { return m_objects_model; } - DynamicPrintConfig* config() const { return m_config; } + ModelConfig* config() const { return m_config; } std::vector* objects() const { return m_objects; } ModelObject* object(const int obj_idx) const ; @@ -320,7 +321,7 @@ public: wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); } wxBoxSizer* get_sizer() {return m_sizer;} int get_selected_obj_idx() const; - DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const; + ModelConfig& get_item_config(const wxDataViewItem& item) const; SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_object_settings); void changed_object(const int obj_idx = -1) const; diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 398cd51d4..481d9f4dc 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -82,7 +82,7 @@ bool ObjectSettings::update_settings_list() return false; const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject; - SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(config, is_object_settings); + SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(&config->get(), is_object_settings); if (!cat_options.empty()) { @@ -176,7 +176,7 @@ bool ObjectSettings::update_settings_list() return true; } -bool ObjectSettings::add_missed_options(DynamicPrintConfig* config_to, const DynamicPrintConfig& config_from) +bool ObjectSettings::add_missed_options(ModelConfig* config_to, const DynamicPrintConfig& config_from) { bool is_added = false; if (wxGetApp().plater()->printer_technology() == ptFFF) @@ -193,7 +193,7 @@ bool ObjectSettings::add_missed_options(DynamicPrintConfig* config_to, const Dyn return is_added; } -void ObjectSettings::update_config_values(DynamicPrintConfig* config) +void ObjectSettings::update_config_values(ModelConfig* config) { const auto objects_model = wxGetApp().obj_list()->GetModel(); const auto item = wxGetApp().obj_list()->GetSelection(); @@ -250,14 +250,12 @@ void ObjectSettings::update_config_values(DynamicPrintConfig* config) { const int obj_idx = objects_model->GetObjectIdByItem(item); assert(obj_idx >= 0); - DynamicPrintConfig* obj_config = &wxGetApp().model().objects[obj_idx]->config; - - main_config.apply(*obj_config, true); + main_config.apply(wxGetApp().model().objects[obj_idx]->config.get(), true); printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) : config_manipulation.update_print_sla_config(&main_config) ; } - main_config.apply(*config, true); + main_config.apply(config->get(), true); printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) : config_manipulation.update_print_sla_config(&main_config) ; diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index ff187eddc..91cfe1dda 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -10,6 +10,7 @@ class wxBoxSizer; namespace Slic3r { class DynamicPrintConfig; +class ModelConfig; namespace GUI { class ConfigOptionsGroup; @@ -52,8 +53,8 @@ public: * Example: if Infill is set to 100%, and Fill Pattern is missed in config_to, * we should add fill_pattern to avoid endless loop in update */ - bool add_missed_options(DynamicPrintConfig *config_to, const DynamicPrintConfig &config_from); - void update_config_values(DynamicPrintConfig*config); + bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from); + void update_config_values(ModelConfig *config); void UpdateAndShow(const bool show) override; void msw_rescale(); }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 04ada5253..3d0d9c79a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -466,7 +466,7 @@ GLGizmoHollow::get_config_options(const std::vector& keys) const if (! mo) return out; - const DynamicPrintConfig& object_cfg = mo->config; + const DynamicPrintConfig& object_cfg = mo->config.get(); const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -556,7 +556,7 @@ RENDER_AGAIN: auto opts = get_config_options({"hollowing_enable"}); m_enable_hollowing = static_cast(opts[0].first)->value; if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { - mo->config.opt("hollowing_enable", true)->value = m_enable_hollowing; + mo->config.set("hollowing_enable", m_enable_hollowing); wxGetApp().obj_list()->update_and_show_object_settings_item(); config_changed = true; } @@ -618,14 +618,14 @@ RENDER_AGAIN: } if (slider_edited || slider_released) { if (slider_released) { - mo->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; - mo->config.opt("hollowing_quality", true)->value = m_quality_stash; - mo->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; + mo->config.set("hollowing_min_thickness", m_offset_stash); + mo->config.set("hollowing_quality", m_quality_stash); + mo->config.set("hollowing_closing_distance", m_closing_d_stash); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); } - mo->config.opt("hollowing_min_thickness", true)->value = offset; - mo->config.opt("hollowing_quality", true)->value = quality; - mo->config.opt("hollowing_closing_distance", true)->value = closing_d; + mo->config.set("hollowing_min_thickness", offset); + mo->config.set("hollowing_quality", quality); + mo->config.set("hollowing_closing_distance", closing_d); if (slider_released) { wxGetApp().obj_list()->update_and_show_object_settings_item(); config_changed = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index bc29da6d2..c9db14968 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -546,7 +546,7 @@ std::vector GLGizmoSlaSupports::get_config_options(const st if (! mo) return out; - const DynamicPrintConfig& object_cfg = mo->config; + const DynamicPrintConfig& object_cfg = mo->config.get(); const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -753,15 +753,15 @@ RENDER_AGAIN: m_density_stash = density; } if (slider_edited) { - mo->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - mo->config.opt("support_points_density_relative", true)->value = (int)density; + mo->config.set("support_points_minimal_distance", minimal_point_distance); + mo->config.set("support_points_density_relative", (int)density); } if (slider_released) { - mo->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; - mo->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; + mo->config.set("support_points_minimal_distance", m_minimal_point_distance_stash); + mo->config.set("support_points_density_relative", (int)m_density_stash); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); - mo->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - mo->config.opt("support_points_density_relative", true)->value = (int)density; + mo->config.set("support_points_minimal_distance", minimal_point_distance); + mo->config.set("support_points_density_relative", (int)density); wxGetApp().obj_list()->update_and_show_object_settings_item(); } diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 14defd9a9..e1c1e9d13 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -414,7 +414,7 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index m_opt_map.emplace(opt_id, pair); if (m_show_modified_btns) // fill group and category values just fro options from Settings Tab - wxGetApp().sidebar().get_searcher().add_key(opt_id, title, config_category); + wxGetApp().sidebar().get_searcher().add_key(opt_id, title, this->config_category()); return Option(*m_config->def()->get(opt_key), opt_id); } @@ -430,13 +430,11 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b return; } - auto itOption = it->second; - std::string opt_key = itOption.first; - int opt_index = itOption.second; + auto itOption = it->second; + const std::string &opt_key = itOption.first; + int opt_index = itOption.second; - auto option = m_options.at(opt_id).opt; - - change_opt_value(*m_config, opt_key, value, opt_index == -1 ? 0 : opt_index); + this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index); } OptionsGroup::on_change_OG(opt_id, value); @@ -470,7 +468,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, opt_key == "bed_shape" || opt_key == "filament_ramming_parameters" || opt_key == "compatible_printers" || opt_key == "compatible_prints" ) { value = get_config_value(config, opt_key); - change_opt_value(*m_config, opt_key, value); + this->change_opt_value(opt_key, value); return; } else @@ -789,6 +787,15 @@ Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int op return opt_id.empty() ? nullptr : get_field(opt_id); } +// Change an option on m_config, possibly call ModelConfig::touch(). +void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) + +{ + Slic3r::GUI::change_opt_value(const_cast(*m_config), opt_key, value, opt_index); + if (m_modelconfig) + m_modelconfig->touch(); +} + void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/) { SetLabel(value); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index edd4a15bc..78434c03f 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -221,18 +221,18 @@ protected: class ConfigOptionsGroup: public OptionsGroup { public: - ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, + ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* config = nullptr, bool is_tab_opt = false, column_t extra_clmn = nullptr) : - OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(_config) {} + OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(config) {} + ConfigOptionsGroup( wxWindow* parent, const wxString& title, ModelConfig* config, + bool is_tab_opt = false, column_t extra_clmn = nullptr) : + OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(&config->get()), m_modelconfig(config) {} - /// reference to libslic3r config, non-owning pointer (?). - DynamicPrintConfig* m_config {nullptr}; - bool m_full_labels {0}; - t_opt_map m_opt_map; + const std::string& config_category() const throw() { return m_config_category; } + const t_opt_map& opt_map() const throw() { return m_opt_map; } - std::string config_category; - - void set_config(DynamicPrintConfig* config) { m_config = config; } + void set_config_category(const std::string &category) { this->m_config_category = category; } + void set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; } Option get_option(const std::string& opt_key, int opt_index = -1); Line create_single_option_line(const std::string& title, int idx = -1) /*const*/{ Option option = get_option(title, idx); @@ -266,6 +266,20 @@ public: // return option value from config boost::any get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1); Field* get_fieldc(const t_config_option_key& opt_key, int opt_index); + +private: + // Reference to libslic3r config or ModelConfig::get(), non-owning pointer. + // The reference is const, so that the spots which modify m_config are clearly + // demarcated by const_cast and m_config_changed_callback is called afterwards. + const DynamicPrintConfig* m_config {nullptr}; + // If the config is modelconfig, then ModelConfig::touch() has to be called after value change. + ModelConfig* m_modelconfig { nullptr }; + bool m_full_labels{ 0 }; + t_opt_map m_opt_map; + std::string m_config_category; + + // Change an option on m_config, possibly call ModelConfig::touch(). + void change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); }; // Static text shown among the options. diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index bef0fd5d7..736be100d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1364,7 +1364,7 @@ void Selection::copy_to_clipboard() ModelObject* dst_object = m_clipboard.add_object(); dst_object->name = src_object->name; dst_object->input_file = src_object->input_file; - static_cast(dst_object->config) = static_cast(src_object->config); + dst_object->config.assign_config(src_object->config); dst_object->sla_support_points = src_object->sla_support_points; dst_object->sla_points_status = src_object->sla_points_status; dst_object->sla_drain_holes = src_object->sla_drain_holes; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 29c9e3302..de268fc7b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -675,8 +675,8 @@ void Tab::update_changed_tree_ui() { if (!sys_page && modified_page) break; - for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { - const std::string& opt_key = it->first; + for (const auto &kvp : group->opt_map()) { + const std::string& opt_key = kvp.first; get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } @@ -764,7 +764,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) is_empty ? m_compatible_prints.btn->Disable() : m_compatible_prints.btn->Enable(); } } - for (auto kvp : group->m_opt_map) { + for (const auto &kvp : group->opt_map()) { const std::string& opt_key = kvp.first; if ((m_options_list[opt_key] & os) == 0) to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); @@ -3791,7 +3791,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la //! config_ have to be "right" ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config, true, extra_column); - optgroup->config_category = m_title.ToStdString(); + optgroup->set_config_category(m_title.ToStdString()); if (noncommon_label_width >= 0) optgroup->label_width = noncommon_label_width; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 79cf3e82e..b6d22d1e6 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -17,7 +17,7 @@ #define CEREAL_FUTURE_EXPERIMENTAL #include -#include +#include #include #include From 94aac4cf97441908c3409f98e4a105ae96864c76 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 15:45:04 +0200 Subject: [PATCH 2/7] What MSVC could process I always wonder. --- src/libslic3r/ObjectID.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/ObjectID.cpp b/src/libslic3r/ObjectID.cpp index 7188f05fd..7177c47fe 100644 --- a/src/libslic3r/ObjectID.cpp +++ b/src/libslic3r/ObjectID.cpp @@ -17,7 +17,7 @@ ObjectID wipe_tower_instance_id() return mine.id(); } -size_t ObjectWithTimestamp::s_last_timestamp = 1; +ObjectWithTimestamp::Timestamp ObjectWithTimestamp::s_last_timestamp = 1; } // namespace Slic3r From aa0335a750cf3703b79820bb2b02d51956d7bb0e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 16:18:56 +0200 Subject: [PATCH 3/7] Trying to fix perl bindings --- xs/xsp/Model.xsp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 844b7c95e..93067ebe3 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -118,7 +118,7 @@ load_stl(CLASS, path, object_name) %code%{ RETVAL = THIS->get_model(); %}; Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &const_cast(THIS->config.get()); %}; std::string get_attribute(std::string name) %code%{ if (THIS->attributes.find(name) != THIS->attributes.end()) RETVAL = THIS->attributes[name]; %}; @@ -202,7 +202,7 @@ ModelMaterial::attributes() void set_input_file(std::string value) %code%{ THIS->input_file = value; %}; Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &const_cast(THIS->config.get()); %}; Ref model() %code%{ RETVAL = THIS->get_model(); %}; @@ -248,7 +248,7 @@ ModelMaterial::attributes() Ref material(); Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &const_cast(THIS->config.get()); %}; Ref mesh() %code%{ RETVAL = &THIS->mesh(); %}; From 7cdc61b67abe04e14c5c2a44db5259cec4901f30 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 16:41:47 +0200 Subject: [PATCH 4/7] Trying to patch the Perl bindings. --- xs/xsp/Print.xsp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index e4957c042..478b1faf8 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -139,7 +139,19 @@ _constant() %}; bool apply(Model *model, DynamicPrintConfig* config) - %code%{ RETVAL = THIS->apply(*model, *config); %}; + %code%{ + // Touching every config as the Perl bindings does not correctly export ModelConfig, + // therefore the configs have often invalid timestamps. + model->config.touch(); + for (auto obj : model->objects) { + obj->config.touch(); + for (auto vol : obj->volumes) + vol->config.touch(); + } + for (auto mat : model->materials) + mat->config.touch(); + RETVAL = THIS->apply(*model, *config); + %}; bool has_infinite_skirt(); std::vector extruders() const; int validate() %code%{ From 8f04a76337d8b29c375fb4b8de240bcd6e8e279d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 14:52:05 +0200 Subject: [PATCH 5/7] Final fix of Perl bindings --- xs/xsp/Print.xsp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 478b1faf8..d9872aa7e 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -142,14 +142,13 @@ _constant() %code%{ // Touching every config as the Perl bindings does not correctly export ModelConfig, // therefore the configs have often invalid timestamps. - model->config.touch(); for (auto obj : model->objects) { obj->config.touch(); for (auto vol : obj->volumes) vol->config.touch(); } for (auto mat : model->materials) - mat->config.touch(); + mat.second->config.touch(); RETVAL = THIS->apply(*model, *config); %}; bool has_infinite_skirt(); From 7a799be426d00c70fe181efb454065925f87c34a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 19:03:09 +0200 Subject: [PATCH 6/7] DynamicPrintConfig::normalize() renamed to normalize_fdm(), optimization of Print::apply() --- src/PrusaSlicer.cpp | 6 +++--- src/libslic3r/Print.cpp | 32 ++++++++++++++++++++------------ src/libslic3r/PrintConfig.cpp | 2 +- src/libslic3r/PrintConfig.hpp | 12 +----------- src/libslic3r/PrintObject.cpp | 34 +++++++++++++++++++++++++++++----- src/libslic3r/SLAPrint.cpp | 8 +++----- src/libslic3r/SLAPrint.hpp | 2 +- 7 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index a12ad8bb7..d79196cfa 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -93,7 +93,7 @@ int CLI::run(int argc, char **argv) return 1; m_extra_config.apply(m_config, true); - m_extra_config.normalize(); + m_extra_config.normalize_fdm(); PrinterTechnology printer_technology = Slic3r::printer_technology(m_config); @@ -129,7 +129,7 @@ int CLI::run(int argc, char **argv) boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl; return 1; } - config.normalize(); + config.normalize_fdm(); PrinterTechnology other_printer_technology = Slic3r::printer_technology(config); if (printer_technology == ptUnknown) { printer_technology = other_printer_technology; @@ -196,7 +196,7 @@ int CLI::run(int argc, char **argv) // (command line options override --load files) m_print_config.apply(m_extra_config, true); // Normalizing after importing the 3MFs / AMFs - m_print_config.normalize(); + m_print_config.normalize_fdm(); // Initialize full print configs for both the FFF and SLA technologies. FullPrintConfig fff_print_config; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fb276a7c2..34a3d8b23 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -587,7 +587,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ new_full_config.option("print_settings_id", true); new_full_config.option("filament_settings_id", true); new_full_config.option("printer_settings_id", true); - new_full_config.normalize(); + new_full_config.normalize_fdm(); // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. t_config_option_keys print_diff, object_diff, region_diff, full_config_diff; @@ -844,8 +844,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked. bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER); - bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER); - bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); + bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) || + model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); if (model_parts_differ || modifiers_differ || model_object.origin_translation != model_object_new.origin_translation || ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) || @@ -858,20 +858,21 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } // Copy content of the ModelObject including its ID, do not change the parent. model_object.assign_copy(model_object_new); - } else if (support_blockers_differ || support_enforcers_differ || model_custom_supports_data_changed(model_object, model_object_new)) { + } else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) { // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. - this->call_cancel_callback(); - update_apply_status(false); + if (supports_differ) { + this->call_cancel_callback(); + update_apply_status(false); + } // 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) update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); - if (support_enforcers_differ || support_blockers_differ) { + if (supports_differ) { // Copy just the support volumes. model_volume_list_update_supports(model_object, model_object_new); } - } - if (model_custom_seam_data_changed(model_object, model_object_new)) { + } else if (model_custom_seam_data_changed(model_object, model_object_new)) { update_apply_status(this->invalidate_step(psGCodeExport)); } if (! model_parts_differ && ! modifiers_differ) { @@ -942,13 +943,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ old.emplace_back(&(*it)); } // Generate a list of trafos and XY offsets for instances of a ModelObject - PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders); + // Producing the config for PrintObject on demand, caching it at print_object_last. + const PrintObject *print_object_last = nullptr; + auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) { + print_object->config_apply(print_object_last ? + print_object_last->config() : + PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders)); + print_object_last = print_object; + }; std::vector new_print_instances = print_objects_from_model_object(*model_object); if (old.empty()) { // Simple case, just generate new instances. for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) { PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances)); - print_object->config_apply(config); + print_object_apply_config(print_object); print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); new_objects = true; @@ -965,7 +973,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ if (it_old == old.end() || ! 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, new_instances.trafo, std::move(new_instances.instances)); - print_object->config_apply(config); + print_object_apply_config(print_object); print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); new_objects = true; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a8eb68521..93d9cfbcf 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3233,7 +3233,7 @@ PrinterTechnology printer_technology(const ConfigBase &cfg) return ptUnknown; } -void DynamicPrintConfig::normalize() +void DynamicPrintConfig::normalize_fdm() { if (this->has("extruder")) { int extruder = this->option("extruder")->getInt(); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index bb4428ab4..52e3bc38c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -254,7 +254,7 @@ public: // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. const ConfigDef* def() const override { return &print_config_def; } - void normalize(); + void normalize_fdm(); void set_num_extruders(unsigned int num_extruders); @@ -269,14 +269,6 @@ public: { PrintConfigDef::handle_legacy(opt_key, value); } }; -template -void normalize_and_apply_config(CONFIG &dst, const DynamicPrintConfig &src) -{ - DynamicPrintConfig src_normalized(src); - src_normalized.normalize(); - dst.apply(src_normalized, true); -} - class StaticPrintConfig : public StaticConfig { public: @@ -1457,8 +1449,6 @@ private: static uint64_t s_last_timestamp; }; -template void normalize_and_apply_config(CONFIG& dst, const ModelConfig& src) { normalize_and_apply_config(dst, src.get()); } - } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c0425ada4..7bf5734bb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1544,22 +1544,46 @@ static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders) { PrintObjectConfig config = default_object_config; - normalize_and_apply_config(config, object.config); + { + DynamicPrintConfig src_normalized(object.config.get()); + src_normalized.normalize_fdm(); + config.apply(src_normalized, true); + } // Clamp invalid extruders to the default extruder (with index 1). clamp_exturder_to_default(config.support_material_extruder, num_extruders); clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders); return config; } +static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in) +{ + // 1) Copy the "extruder key to infill_extruder and perimeter_extruder. + std::string sextruder = "extruder"; + auto *opt_extruder = in.opt(sextruder); + if (opt_extruder) { + if (opt_extruder->value != 0) { + out.infill_extruder.value = opt_extruder->value; + out.perimeter_extruder.value = opt_extruder->value; + } + } + // 2) Copy the rest of the values. + for (auto it = in.cbegin(); it != in.cend(); ++ it) + if (it->first != sextruder) { + ConfigOption *my_opt = out.option(it->first, false); + if (my_opt) + my_opt->set(it->second.get()); + } +} + PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders) { PrintRegionConfig config = default_region_config; - normalize_and_apply_config(config, volume.get_object()->config); + apply_to_print_region_config(config, volume.get_object()->config.get()); if (layer_range_config != nullptr) - normalize_and_apply_config(config, *layer_range_config); - normalize_and_apply_config(config, volume.config); + apply_to_print_region_config(config, *layer_range_config); + apply_to_print_region_config(config, volume.config.get()); if (! volume.material_id().empty()) - normalize_and_apply_config(config, volume.material()->config); + apply_to_print_region_config(config, volume.material()->config.get()); // Clamp invalid extruders to the default extruder (with index 1). clamp_exturder_to_default(config.infill_extruder, num_extruders); clamp_exturder_to_default(config.perimeter_extruder, num_extruders); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 30d1df6c1..a2d2ff620 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -185,7 +185,6 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con config.option("sla_print_settings_id", true); config.option("sla_material_settings_id", true); config.option("printer_settings_id", true); - config.normalize(); // Collect changes to print config. t_config_option_keys print_diff = m_print_config.diff(config); t_config_option_keys printer_diff = m_printer_config.diff(config); @@ -400,7 +399,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con model_object.config.assign_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); + new_config.apply(model_object.config.get(), true); 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()) { @@ -464,9 +463,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con print_object->set_instances(std::move(new_instances)); - SLAPrintObjectConfig new_config = m_default_object_config; - normalize_and_apply_config(new_config, model_object.config); - print_object->config_apply(new_config, true); + print_object->config_apply(m_default_object_config, true); + print_object->config_apply(model_object.config.get(), true); print_objects_new.emplace_back(print_object); new_objects = true; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 5fbf8346c..5ca1b2b8e 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -261,7 +261,7 @@ protected: SLAPrintObject(SLAPrint* print, ModelObject* model_object); ~SLAPrintObject(); - void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } + void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); } 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); } From e0b0a2cdcfa87be770c64a66146cd6e5e01252c8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 24 Sep 2020 20:32:52 +0200 Subject: [PATCH 7/7] Fix of previous commit. --- src/libslic3r/PrintObject.cpp | 10 ++++++---- xs/t/15_config.t | 6 +++--- xs/xsp/Config.xsp | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7bf5734bb..6f26d8b9d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -440,7 +440,7 @@ std::pair PrintObject::prepare using namespace FillAdaptive; auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this); - if (adaptive_line_spacing == 0. && support_line_spacing == 0. || this->layers().empty()) + if ((adaptive_line_spacing == 0. && support_line_spacing == 0.) || this->layers().empty()) return std::make_pair(OctreePtr(), OctreePtr()); indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set(); @@ -1561,9 +1561,11 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr std::string sextruder = "extruder"; auto *opt_extruder = in.opt(sextruder); if (opt_extruder) { - if (opt_extruder->value != 0) { - out.infill_extruder.value = opt_extruder->value; - out.perimeter_extruder.value = opt_extruder->value; + int extruder = opt_extruder->value; + if (extruder != 0) { + out.infill_extruder .value = extruder; + out.solid_infill_extruder.value = extruder; + out.perimeter_extruder .value = extruder; } } // 2) Copy the rest of the values. diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 1f9fc939b..55b679101 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -219,7 +219,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo my $config = Slic3r::Config->new; $config->set('extruder', 2); $config->set('perimeter_extruder', 3); - $config->normalize; + $config->normalize_fdm; ok !$config->has('extruder'), 'extruder option is removed after normalize()'; is $config->get('infill_extruder'), 2, 'undefined extruder is populated with default extruder'; is $config->get('perimeter_extruder'), 3, 'defined extruder is not overwritten by default extruder'; @@ -228,7 +228,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo { my $config = Slic3r::Config->new; $config->set('infill_extruder', 2); - $config->normalize; + $config->normalize_fdm; is $config->get('solid_infill_extruder'), 2, 'undefined solid infill extruder is populated with infill extruder'; } @@ -236,7 +236,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo my $config = Slic3r::Config->new; $config->set('spiral_vase', 1); $config->set('retract_layer_change', [1,0]); - $config->normalize; + $config->normalize_fdm; is_deeply $config->get('retract_layer_change'), [0,0], 'retract_layer_change is disabled with spiral_vase'; } diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 63dc5b312..b8f996797 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -48,7 +48,7 @@ %code{% THIS->apply(*other, true); %}; %name{get_keys} std::vector keys(); void erase(t_config_option_key opt_key); - void normalize(); + void normalize_fdm(); %name{setenv} void setenv_(); double min_object_distance() %code{% RETVAL = Slic3r::min_object_distance(*THIS); %}; static DynamicPrintConfig* load(char *path)