From 059bdb4711e6ca392c707b70ab6d450a96bb85c9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Feb 2020 14:43:58 +0100 Subject: [PATCH] Implemented handling of complex ConfigOptionFloatOrPercent chains by the PlaceholderParser. Namely, all the options with the "ratio_over" reference are now handled correctly by the PlaceholderParser with the exception of the "first_layer_extrusion_width", which overrides speed of extrusions by their respective extrusion type. Also the various extrusion widths (extrusion_width, first_layer_extrusion_width, external_perimeter_extrusion_width etc.) produce the same numbers as if ran through the back-end, with the assumption of not overriding layer height by the variable layer height editing tool or layer height modifiers. --- src/PrusaSlicer.cpp | 2 +- src/libslic3r/Config.cpp | 6 ++ src/libslic3r/Config.hpp | 67 +++++++++++---- src/libslic3r/Flow.cpp | 95 ++++++++++++++++++++- src/libslic3r/Flow.hpp | 10 +++ src/libslic3r/PlaceholderParser.cpp | 72 ++++++++++------ src/libslic3r/Print.cpp | 19 +---- src/libslic3r/Print.hpp | 1 - src/libslic3r/PrintConfig.cpp | 3 +- src/libslic3r/PrintConfig.hpp | 27 +----- tests/libslic3r/test_placeholder_parser.cpp | 23 ++++- 11 files changed, 236 insertions(+), 89 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 28aadc045..7662288d3 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -591,7 +591,7 @@ bool CLI::setup(int argc, char **argv) // Initialize with defaults. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) for (const std::pair &optdef : *options) - m_config.optptr(optdef.first, true); + m_config.option(optdef.first, true); set_data_dir(m_config.opt_string("datadir")); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index bcd017278..6806f4f54 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -757,6 +757,12 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre return opt; } +const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) const +{ + auto it = options.find(opt_key); + return (it == options.end()) ? nullptr : it->second.get(); +} + void DynamicConfig::read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys) { std::vector args; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 9b04ae026..1258cc3f1 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1494,8 +1494,49 @@ protected: ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type); }; +// A pure interface to resolving ConfigOptions. +// This pure interface is useful as a base of ConfigBase, also it may be overriden to combine +// various config sources. +class ConfigOptionResolver +{ +public: + ConfigOptionResolver() {} + virtual ~ConfigOptionResolver() {} + + // Find a ConfigOption instance for a given name. + virtual const ConfigOption* optptr(const t_config_option_key &opt_key) const = 0; + + bool has(const t_config_option_key &opt_key) const { return this->optptr(opt_key) != nullptr; } + + const ConfigOption* option(const t_config_option_key &opt_key) const { return this->optptr(opt_key); } + + template + const TYPE* option(const t_config_option_key& opt_key) const + { + const ConfigOption* opt = this->optptr(opt_key); + return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast(opt); + } + + const ConfigOption* option_throw(const t_config_option_key& opt_key) const + { + const ConfigOption* opt = this->optptr(opt_key); + if (opt == nullptr) + throw UnknownOptionException(opt_key); + return opt; + } + + template + const TYPE* option_throw(const t_config_option_key& opt_key) const + { + const ConfigOption* opt = this->option_throw(opt_key); + if (opt->type() != TYPE::static_type()) + throw BadOptionTypeException("Conversion to a wrong type"); + return static_cast(opt); + } +}; + // An abstract configuration store. -class ConfigBase +class ConfigBase : public ConfigOptionResolver { public: // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. @@ -1503,7 +1544,7 @@ public: // but it carries the defaults of the configuration values. ConfigBase() {} - virtual ~ConfigBase() {} + ~ConfigBase() override {} // Virtual overridables: public: @@ -1513,6 +1554,7 @@ public: virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; // Collect names of all configuration values maintained by this configuration store. virtual t_config_option_keys keys() const = 0; + protected: // Verify whether the opt_key has not been obsoleted or renamed. // Both opt_key and value may be modified by handle_legacy(). @@ -1521,12 +1563,10 @@ protected: virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {} public: + using ConfigOptionResolver::option; + using ConfigOptionResolver::option_throw; + // Non-virtual methods: - bool has(const t_config_option_key &opt_key) const { return this->option(opt_key) != nullptr; } - - const ConfigOption* option(const t_config_option_key &opt_key) const - { return const_cast(this)->option(opt_key, false); } - ConfigOption* option(const t_config_option_key &opt_key, bool create = false) { return this->optptr(opt_key, create); } @@ -1537,10 +1577,6 @@ public: return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast(opt); } - template - const TYPE* option(const t_config_option_key &opt_key) const - { return const_cast(this)->option(opt_key, false); } - ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false) { ConfigOption *opt = this->optptr(opt_key, create); @@ -1549,9 +1585,6 @@ public: return opt; } - const ConfigOption* option_throw(const t_config_option_key &opt_key) const - { return const_cast(this)->option_throw(opt_key, false); } - template TYPE* option_throw(const t_config_option_key &opt_key, bool create = false) { @@ -1561,10 +1594,6 @@ public: return static_cast(opt); } - template - const TYPE* option_throw(const t_config_option_key &opt_key) const - { return const_cast(this)->option_throw(opt_key, false); } - // Apply all keys of other ConfigBase defined by this->def() to this ConfigBase. // An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(), // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. @@ -1735,6 +1764,8 @@ public: { return dynamic_cast(this->option(opt_key, create)); } template const T* opt(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key)); } + // Overrides ConfigResolver::optptr(). + const ConfigOption* optptr(const t_config_option_key &opt_key) const override; // Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override; // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 2c5c64fe7..9ac91c51d 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -1,12 +1,17 @@ #include "Flow.hpp" +#include "I18N.hpp" #include "Print.hpp" #include #include + +// Mark string for localization and translate. +#define L(s) Slic3r::I18N::translate(s) + namespace Slic3r { // This static method returns a sane extrusion width default. -static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, float height) +float Flow::auto_extrusion_width(FlowRole role, float nozzle_diameter) { switch (role) { case frSupportMaterial: @@ -22,6 +27,92 @@ static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, f } } +// Used by the Flow::extrusion_width() funtion to provide hints to the user on default extrusion width values, +// and to provide reasonable values to the PlaceholderParser. +static inline FlowRole opt_key_to_flow_role(const std::string &opt_key) +{ + if (opt_key == "perimeter_extrusion_width" || + // or all the defaults: + opt_key == "extrusion_width" || opt_key == "first_layer_extrusion_width") + return frPerimeter; + else if (opt_key == "external_perimeter_extrusion_width") + return frExternalPerimeter; + else if (opt_key == "infill_extrusion_width") + return frInfill; + else if (opt_key == "solid_infill_extrusion_width") + return frSolidInfill; + else if (opt_key == "top_infill_extrusion_width") + return frTopSolidInfill; + else if (opt_key == "support_material_extrusion_width") + return frSupportMaterial; + else + throw std::runtime_error("opt_key_to_flow_role: invalid argument"); +}; + +static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key) +{ + throw std::runtime_error((boost::format(L("Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible.")) % opt_key % dependent_opt_key).str()); +} + +// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser. +double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloatOrPercent* opt, const ConfigOptionResolver& config, const unsigned int first_printing_extruder) +{ + assert(opt != nullptr); + + bool first_layer = boost::starts_with(opt_key, "first_layer_"); + +#if 0 +// This is the logic used for skit / brim, but not for the rest of the 1st layer. + if (opt->value == 0. && first_layer) { + // The "first_layer_extrusion_width" was set to zero, try a substitute. + opt = config.option("perimeter_extrusion_width"); + if (opt == nullptr) + throw_on_missing_variable(opt_key, "perimeter_extrusion_width"); + } +#endif + + if (opt->value == 0.) { + // The role specific extrusion width value was set to zero, try the role non-specific extrusion width. + opt = config.option("extrusion_width"); + if (opt == nullptr) + throw_on_missing_variable(opt_key, "extrusion_width"); + // Use the "layer_height" instead of "first_layer_height". + first_layer = false; + } + + if (opt->percent) { + auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height"; + auto opt_layer_height = config.option(opt_key_layer_height); + if (opt_layer_height == nullptr) + throw_on_missing_variable(opt_key, opt_key_layer_height); + double layer_height = opt_layer_height->getFloat(); + if (first_layer && static_cast(opt_layer_height)->percent) { + // first_layer_height depends on layer_height. + opt_layer_height = config.option("layer_height"); + if (opt_layer_height == nullptr) + throw_on_missing_variable(opt_key, "layer_height"); + layer_height *= 0.01 * opt_layer_height->getFloat(); + } + return opt->get_abs_value(layer_height); + } + + if (opt->value == 0.) { + // If user left option to 0, calculate a sane default width. + auto opt_nozzle_diameters = config.option("nozzle_diameter"); + if (opt_nozzle_diameters == nullptr) + throw_on_missing_variable(opt_key, "nozzle_diameter"); + return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder))); + } + + return opt->value; +} + +// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser. +double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder) +{ + return extrusion_width(opt_key, config.option(opt_key), config, first_printing_extruder); +} + // This constructor builds a Flow object from an extrusion width config setting // and other context properties. Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) @@ -39,7 +130,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent sqrt(bridge_flow_ratio) * nozzle_diameter; } else if (! width.percent && width.value == 0.) { // If user left option to 0, calculate a sane default width. - w = auto_extrusion_width(role, nozzle_diameter, height); + w = auto_extrusion_width(role, nozzle_diameter); } else { // If user set a manual value, use it. w = float(width.get_abs_value(height)); diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 51cc4da9d..aad189775 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -64,6 +64,16 @@ public: // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale // to fit a region with integer number of lines. static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + + // Sane extrusion width defautl based on nozzle diameter. + // The defaults were derived from manual Prusa MK3 profiles. + static float auto_extrusion_width(FlowRole role, float nozzle_diameter); + + // Extrusion width from full config, taking into account the defaults (when set to zero) and ratios (percentages). + // Precise value depends on layer index (1st layer vs. other layers vs. variable layer height), + // on active extruder etc. Therefore the value calculated by this function shall be used as a hint only. + static double extrusion_width(const std::string &opt_key, const ConfigOptionFloatOrPercent *opt, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); + static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); }; extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index db3134ded..527d82b4c 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,4 +1,5 @@ #include "PlaceholderParser.hpp" +#include "Flow.hpp" #include #include #include @@ -99,11 +100,7 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf const ConfigOption *opt_old = config_old.option(opt_key); const ConfigOption *opt_new = config_new.option(opt_key); assert(opt_new != nullptr); - if (opt_old == nullptr) - return false; - return (opt_new->type() == coFloatOrPercent) ? - dynamic_cast(opt_old)->value == config_new.get_abs_value(opt_key) : - *opt_new == *opt_old; + return opt_old != nullptr && *opt_new == *opt_old; } std::vector PlaceholderParser::config_diff(const DynamicPrintConfig &rhs) @@ -126,14 +123,7 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) bool modified = false; for (const t_config_option_key &opt_key : rhs.keys()) { if (! opts_equal(m_config, rhs, opt_key)) { - // Store a copy of the config option. - // Convert FloatOrPercent values to floats first. - //FIXME there are some ratio_over chains, which end with empty ratio_with. - // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - const ConfigOption *opt_rhs = rhs.option(opt_key); - this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? - new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : - opt_rhs->clone()); + this->set(opt_key, rhs.option(opt_key)->clone()); modified = true; } } @@ -142,16 +132,8 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vector &keys) { - for (const t_config_option_key &opt_key : keys) { - // Store a copy of the config option. - // Convert FloatOrPercent values to floats first. - //FIXME there are some ratio_over chains, which end with empty ratio_with. - // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - const ConfigOption *opt_rhs = rhs.option(opt_key); - this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? - new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : - opt_rhs->clone()); - } + for (const t_config_option_key &opt_key : keys) + this->set(opt_key, rhs.option(opt_key)->clone()); } void PlaceholderParser::apply_config(DynamicPrintConfig &&rhs) @@ -635,7 +617,7 @@ namespace client return os; } - struct MyContext { + struct MyContext : public ConfigOptionResolver { const DynamicConfig *external_config = nullptr; const DynamicConfig *config = nullptr; const DynamicConfig *config_override = nullptr; @@ -650,7 +632,7 @@ namespace client static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; } - const ConfigOption* resolve_symbol(const std::string &opt_key) const + const ConfigOption* optptr(const t_config_option_key &opt_key) const override { const ConfigOption *opt = nullptr; if (config_override != nullptr) @@ -662,6 +644,8 @@ namespace client return opt; } + const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } + template static void legacy_variable_expansion( const MyContext *ctx, @@ -758,7 +742,43 @@ namespace client case coPoint: output.set_s(opt.opt->serialize()); break; case coBool: output.set_b(opt.opt->getBool()); break; case coFloatOrPercent: - ctx->throw_exception("FloatOrPercent variables are not supported", opt.it_range); + { + std::string opt_key(opt.it_range.begin(), opt.it_range.end()); + if (boost::ends_with(opt_key, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); + } else if (! static_cast(opt.opt)->percent) { + // Not a percent, just return the value. + output.set_d(opt.opt->getFloat()); + } else { + // Resolve dependencies using the "ratio_over" link to a parent value. + const ConfigOptionDef *opt_def = print_config_def.get(opt_key); + assert(opt_def != nullptr); + double v = opt.opt->getFloat() * 0.01; // percent to ratio + for (;;) { + const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); + if (opt_parent == nullptr) + ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); + if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + assert(opt_parent->type() == coFloatOrPercent); + v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); + break; + } + if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { + v *= opt_parent->getFloat(); + if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) + break; + v *= 0.01; // percent to ratio + } + // Continue one level up in the "ratio_over" hierarchy. + opt_def = print_config_def.get(opt_def->ratio_over); + assert(opt_def != nullptr); + } + output.set_d(v); + } + break; + } default: ctx->throw_exception("Unknown scalar variable type", opt.it_range); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3131fd3d1..2e6d5f8af 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -24,8 +24,7 @@ #include #include -//! macro used to mark string used at localization, -//! return same string +// Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -527,7 +526,6 @@ void Print::config_diffs( const DynamicPrintConfig &new_full_config, t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff, t_config_option_keys &full_config_diff, - DynamicPrintConfig &placeholder_parser_overrides, DynamicPrintConfig &filament_overrides) const { // Collect changes to print config, account for overrides of extruder retract values by filament presets. @@ -563,19 +561,11 @@ void Print::config_diffs( object_diff = m_default_object_config.diff(new_full_config); region_diff = m_default_region_config.diff(new_full_config); // Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser. - // As the PlaceholderParser does not interpret the FloatOrPercent values itself, these values are stored into the PlaceholderParser converted to floats. for (const t_config_option_key &opt_key : new_full_config.keys()) { const ConfigOption *opt_old = m_full_print_config.option(opt_key); const ConfigOption *opt_new = new_full_config.option(opt_key); if (opt_old == nullptr || *opt_new != *opt_old) full_config_diff.emplace_back(opt_key); - if (opt_new->type() == coFloatOrPercent) { - // The m_placeholder_parser is never modified by the background processing, GCode.cpp/hpp makes a copy. - const ConfigOption *opt_old_pp = this->placeholder_parser().config().option(opt_key); - double new_value = new_full_config.get_abs_value(opt_key); - if (opt_old_pp == nullptr || static_cast(opt_old_pp)->value != new_value) - placeholder_parser_overrides.set_key_value(opt_key, new ConfigOptionFloat(new_value)); - } } } @@ -593,8 +583,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // 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; - DynamicPrintConfig placeholder_parser_overrides, filament_overrides; - this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, placeholder_parser_overrides, filament_overrides); + DynamicPrintConfig filament_overrides; + this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides); // Do not use the ApplyStatus as we will use the max function when updating apply_status. unsigned int apply_status = APPLY_STATUS_UNCHANGED; @@ -614,9 +604,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // which should be stopped if print_diff is not empty. size_t num_extruders = m_config.nozzle_diameter.size(); bool num_extruders_changed = false; - if (! full_config_diff.empty() || ! placeholder_parser_overrides.empty()) { + if (! full_config_diff.empty()) { update_apply_status(this->invalidate_step(psGCodeExport)); - m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides)); // Set the profile aliases for the PrintBase::output_filename() m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 75807cdda..1386b798e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -435,7 +435,6 @@ private: const DynamicPrintConfig &new_full_config, t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff, t_config_option_keys &full_config_diff, - DynamicPrintConfig &placeholder_parser_overrides, DynamicPrintConfig &filament_overrides) const; bool invalidate_state_by_config_options(const std::vector &opt_keys); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 47a72b829..ebb05772f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -718,8 +718,9 @@ void PrintConfigDef::init_fff_params() def->gui_type = "f_enum_open"; def->gui_flags = "show_value"; def->enum_values.push_back("PLA"); - def->enum_values.push_back("ABS"); def->enum_values.push_back("PET"); + def->enum_values.push_back("ABS"); + def->enum_values.push_back("ASA"); def->enum_values.push_back("FLEX"); def->enum_values.push_back("HIPS"); def->enum_values.push_back("EDGE"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8d14969c9..5130d3b05 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -46,12 +46,6 @@ enum SeamPosition { spRandom, spNearest, spAligned, spRear }; -/* -enum FilamentType { - ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA -}; -*/ - enum SLAMaterial { slamTough, slamFlex, @@ -149,24 +143,6 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::ge return keys_map; } -/* -template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { - static t_config_enum_values keys_map; - if (keys_map.empty()) { - keys_map["PLA"] = ftPLA; - keys_map["ABS"] = ftABS; - keys_map["PET"] = ftPET; - keys_map["HIPS"] = ftHIPS; - keys_map["FLEX"] = ftFLEX; - keys_map["SCAFF"] = ftSCAFF; - keys_map["EDGE"] = ftEDGE; - keys_map["NGEN"] = ftNGEN; - keys_map["PVA"] = ftPVA; - } - return keys_map; -} -*/ - template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static const t_config_enum_values keys_map = { { "landscape", sladoLandscape}, @@ -354,6 +330,9 @@ protected: #define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ + const ConfigOption* optptr(const t_config_option_key &opt_key) const override \ + { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ + /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 4d8217c16..e632dc705 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -14,7 +14,15 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { { "nozzle_diameter", "0.6;0.6;0.6;0.6" }, { "temperature", "357;359;363;378" } }); - parser.apply_config(config); + // To test the "first_layer_extrusion_width" over "first_layer_heigth" over "layer_height" chain. + config.option("first_layer_height")->value = 150.; + config.option("first_layer_height")->percent = true; + // To let the PlaceholderParser throw when referencing first_layer_speed if it is set to percent, as the PlaceholderParser does not know + // a percent to what. + config.option("first_layer_speed")->value = 50.; + config.option("first_layer_speed")->percent = true; + + parser.apply_config(config); parser.set("foo", 0); parser.set("bar", 2); parser.set("num_extruders", 4); @@ -41,6 +49,19 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); } SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); } + // Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions. + // first_layer_extrusion_width ratio_over first_layer_heigth ratio_over layer_height + SECTION("perimeter_extrusion_width") { REQUIRE(std::stod(parser.process("{perimeter_extrusion_width}")) == Approx(0.67500001192092896)); } + SECTION("first_layer_extrusion_width") { REQUIRE(std::stod(parser.process("{first_layer_extrusion_width}")) == Approx(0.9)); } + SECTION("support_material_xy_spacing") { REQUIRE(std::stod(parser.process("{support_material_xy_spacing}")) == Approx(0.3375)); } + // external_perimeter_speed over perimeter_speed + SECTION("external_perimeter_speed") { REQUIRE(std::stod(parser.process("{external_perimeter_speed}")) == Approx(30.)); } + // infill_overlap over perimeter_extrusion_width + SECTION("infill_overlap") { REQUIRE(std::stod(parser.process("{infill_overlap}")) == Approx(0.16875)); } + // If first_layer_speed is set to percent, then it is applied over respective extrusion types by overriding their respective speeds. + // The PlaceholderParser has no way to know which extrusion type the caller has in mind, therefore it throws. + SECTION("first_layer_speed") { REQUIRE_THROWS(parser.process("{first_layer_speed}")); } + // Test the boolean expression parser. auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); };