diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 09d283a327..d8afe8e021 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -320,7 +321,7 @@ typedef ConfigOption* ConfigOptionPtr; typedef const ConfigOption* ConfigOptionConstPtr; // Value of a single valued option (bool, int, float, string, point, enum) -template +template class ConfigOptionSingle : public ConfigOption { public: T value; @@ -349,6 +350,49 @@ public: size_t hash() const throw() override { return std::hash{}(this->value); } + // Is this option overridden by another option? + // An option overrides another option if it is not nil and not equal. + bool overriden_by(const ConfigOption *rhs) const override { + if (this->nullable()) + throw ConfigurationError("Cannot override a nullable ConfigOption."); + if (rhs->type() != this->type()) + throw ConfigurationError("ConfigOptionVector.overriden_by() applied to different types."); + auto rhs_vec = static_cast*>(rhs); + if (! rhs->nullable()) + // Overridding a non-nullable object with another non-nullable object. + return this->value != rhs_vec->value; + + return false; + } + // Apply an override option, possibly a nullable one. + bool apply_override(const ConfigOption *rhs) override { + if (this->nullable()) + throw ConfigurationError("Cannot override a nullable ConfigOption."); + if (rhs->type() != this->type()) + throw ConfigurationError("ConfigOptionVector.apply_override() applied to different types."); + auto rhs_vec = static_cast*>(rhs); + if (! rhs->nullable()) { + // Overridding a non-nullable object with another non-nullable object. + if (this->value != rhs_vec->value) { + this->value = rhs_vec->value; + return true; + } + return false; + } + + return false; + } + + bool nullable() const override { return NULLABLE; } + + static T nil_value() { return std::numeric_limits::min(); } + + // A scalar is nil, or all values of a vector are nil. + bool is_nil() const override + { + return this->value == nil_value(); + } + private: friend class cereal::access; template void serialize(Archive & ar) { ar(this->value); } @@ -572,18 +616,19 @@ private: template void serialize(Archive & ar) { ar(this->values); } }; -class ConfigOptionFloat : public ConfigOptionSingle +template +class ConfigOptionFloatTempl : public ConfigOptionSingle { public: - ConfigOptionFloat() : ConfigOptionSingle(0) {} - explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {} + ConfigOptionFloatTempl() : ConfigOptionSingle(0) {} + explicit ConfigOptionFloatTempl(double _value) : ConfigOptionSingle(_value) {} static ConfigOptionType static_type() { return coFloat; } ConfigOptionType type() const override { return static_type(); } double getFloat() const override { return this->value; } - ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } - bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; } + ConfigOption* clone() const override { return new ConfigOptionFloatTempl(*this); } + bool operator==(const ConfigOptionFloatTempl &rhs) const throw() { return this->value == rhs.value; } + bool operator< (const ConfigOptionFloatTempl &rhs) const throw() { return this->value < rhs.value; } std::string serialize() const override { @@ -600,7 +645,7 @@ public: return !iss.fail(); } - ConfigOptionFloat& operator=(const ConfigOption *opt) + ConfigOptionFloatTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; @@ -734,6 +779,8 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; +using ConfigOptionFloat = ConfigOptionFloatTempl; +using ConfigOptionFloatNullable = ConfigOptionFloatTempl; using ConfigOptionFloats = ConfigOptionFloatsTempl; using ConfigOptionFloatsNullable = ConfigOptionFloatsTempl; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index f8fef6ac7b..f0443fd72f 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -8,6 +8,7 @@ #include "CSGMesh/CSGMeshCopy.hpp" #include "CSGMesh/PerformCSGMeshBooleans.hpp" #include "format.hpp" +#include "StaticMap.hpp" #include "Format/SLAArchiveFormatRegistry.hpp" @@ -202,6 +203,51 @@ std::vector SLAPrint::print_object_ids() const return out; } +static t_config_option_keys print_config_diffs(const SLAPrintObjectConfig ¤t_config, + const DynamicPrintConfig &new_full_config, + DynamicPrintConfig &material_overrides) +{ + using namespace std::string_view_literals; + + static const constexpr StaticSet overriden_keys = { + "support_pillar_diameter"sv + }; + + static constexpr auto material_ow_prefix = "material_ow_"; + + t_config_option_keys print_diff; + for (const t_config_option_key &opt_key : current_config.keys()) { + const ConfigOption *opt_old = current_config.option(opt_key); + assert(opt_old != nullptr); + const ConfigOption *opt_new = new_full_config.option(opt_key); + // assert(opt_new != nullptr); + if (opt_new == nullptr) + //FIXME This may happen when executing some test cases. + continue; + const ConfigOption *opt_new_override = std::binary_search(overriden_keys.begin(), overriden_keys.end(), opt_key) ? new_full_config.option(material_ow_prefix + opt_key) : nullptr; + if (opt_new_override != nullptr && ! opt_new_override->is_nil()) { + // An override is available at some of the material presets. + bool overriden = opt_new->overriden_by(opt_new_override); + if (overriden || *opt_old != *opt_new) { + auto opt_copy = opt_new->clone(); + opt_copy->apply_override(opt_new_override); + bool changed = *opt_old != *opt_copy; + if (changed) + print_diff.emplace_back(opt_key); + if (changed || overriden) { + // overrides will be applied to the placeholder parser, which layers these parameters over full_print_config. + material_overrides.set_key_value(opt_key, opt_copy); + } else + delete opt_copy; + } + } else if (*opt_new != *opt_old) + print_diff.emplace_back(opt_key); + } + + return print_diff; +} + + SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config) { #ifdef _DEBUG @@ -737,66 +783,68 @@ void SLAPrint::process() bool SLAPrint::invalidate_state_by_config_options(const std::vector &opt_keys, bool &invalidate_all_model_objects) { + using namespace std::string_view_literals; + if (opt_keys.empty()) return false; - static std::unordered_set steps_full = { - "initial_layer_height", - "material_correction", - "material_correction_x", - "material_correction_y", - "material_correction_z", - "material_print_speed", - "relative_correction", - "relative_correction_x", - "relative_correction_y", - "relative_correction_z", - "absolute_correction", - "elefant_foot_compensation", - "elefant_foot_min_width", - "gamma_correction" + static constexpr StaticSet steps_full = { + "initial_layer_height"sv, + "material_correction"sv, + "material_correction_x"sv, + "material_correction_y"sv, + "material_correction_z"sv, + "material_print_speed"sv, + "relative_correction"sv, + "relative_correction_x"sv, + "relative_correction_y"sv, + "relative_correction_z"sv, + "absolute_correction"sv, + "elefant_foot_compensation"sv, + "elefant_foot_min_width"sv, + "gamma_correction"sv }; // Cache the plenty of parameters, which influence the final rasterization only, // or they are only notes not influencing the rasterization step. - static std::unordered_set steps_rasterize = { - "min_exposure_time", - "max_exposure_time", - "exposure_time", - "min_initial_exposure_time", - "max_initial_exposure_time", - "initial_exposure_time", - "display_width", - "display_height", - "display_pixels_x", - "display_pixels_y", - "display_mirror_x", - "display_mirror_y", - "display_orientation", - "sla_archive_format", - "sla_output_precision" + static constexpr StaticSet steps_rasterize = { + "min_exposure_time"sv, + "max_exposure_time"sv, + "exposure_time"sv, + "min_initial_exposure_time"sv, + "max_initial_exposure_time"sv, + "initial_exposure_time"sv, + "display_width"sv, + "display_height"sv, + "display_pixels_x"sv, + "display_pixels_y"sv, + "display_mirror_x"sv, + "display_mirror_y"sv, + "display_orientation"sv, + "sla_archive_format"sv, + "sla_output_precision"sv }; - static std::unordered_set steps_ignore = { - "bed_shape", - "max_print_height", - "printer_technology", - "output_filename_format", - "fast_tilt_time", - "slow_tilt_time", - "high_viscosity_tilt_time", - "area_fill", - "bottle_cost", - "bottle_volume", - "bottle_weight", - "material_density" + static StaticSet steps_ignore = { + "bed_shape"sv, + "max_print_height"sv, + "printer_technology"sv, + "output_filename_format"sv, + "fast_tilt_time"sv, + "slow_tilt_time"sv, + "high_viscosity_tilt_time"sv, + "area_fill"sv, + "bottle_cost"sv, + "bottle_volume"sv, + "bottle_weight"sv, + "material_density"sv }; std::vector steps; std::vector osteps; bool invalidated = false; - for (const t_config_option_key &opt_key : opt_keys) { + for (std::string_view opt_key : opt_keys) { if (steps_rasterize.find(opt_key) != steps_rasterize.end()) { // These options only affect the final rasterization, or they are just notes without influence on the output, // so there is nothing to invalidate.