diff options
author | bubnikv <bubnikv@gmail.com> | 2016-11-01 15:41:24 +0300 |
---|---|---|
committer | bubnikv <bubnikv@gmail.com> | 2016-11-01 15:41:24 +0300 |
commit | 3d3654707b97f679037cb66c2143338cf8baf917 (patch) | |
tree | e13ed4e8725d1f4d4c1b713f518ac5167e72e16a /xs | |
parent | 4e66ed81d26774a6196259dee35a5da0c678d081 (diff) |
Added "Notes" page to the filament configuration.
Added "filament_max_volumetric_speed", a cap on the maximum volumetric
extrusion role, filament specific. This is very useful when mixing
rigid filament with a soft filament.
Extended the import / export of multi-string values into configuration
values, including the test cases. Multi-line strings will be enclosed
into quotes, quotes escaped using a C-style escape sequences. Single
word strings could still be stored without quotes.
Diffstat (limited to 'xs')
-rw-r--r-- | xs/src/libslic3r/Config.cpp | 165 | ||||
-rw-r--r-- | xs/src/libslic3r/Config.hpp | 53 | ||||
-rw-r--r-- | xs/src/libslic3r/GCode.cpp | 7 | ||||
-rw-r--r-- | xs/src/libslic3r/PrintConfig.cpp | 25 | ||||
-rw-r--r-- | xs/src/libslic3r/PrintConfig.hpp | 4 | ||||
-rw-r--r-- | xs/t/15_config.t | 46 |
6 files changed, 255 insertions, 45 deletions
diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 39c0da2bb..cdeee4dd8 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -8,6 +8,159 @@ namespace Slic3r { +std::string escape_string_cstyle(const std::string &str) +{ + // Allocate a buffer twice the input string length, + // so the output will fit even if all input characters get escaped. + std::vector<char> out(str.size() * 2, 0); + char *outptr = out.data(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == '\n' || c == '\r') { + (*outptr ++) = '\\'; + (*outptr ++) = 'n'; + } else + (*outptr ++) = c; + } + return std::string(out.data(), outptr - out.data()); +} + +std::string escape_strings_cstyle(const std::vector<std::string> &strs) +{ + // 1) Estimate the output buffer size to avoid buffer reallocation. + size_t outbuflen = 0; + for (size_t i = 0; i < strs.size(); ++ i) + // Reserve space for every character escaped + quotes + semicolon. + outbuflen += strs[i].size() * 2 + 3; + // 2) Fill in the buffer. + std::vector<char> out(outbuflen, 0); + char *outptr = out.data(); + for (size_t j = 0; j < strs.size(); ++ j) { + if (j > 0) + // Separate the strings. + (*outptr ++) = ';'; + const std::string &str = strs[j]; + // Is the string simple or complex? Complex string contains spaces, tabs, new lines and other + // escapable characters. Empty string shall be quoted as well, if it is the only string in strs. + bool should_quote = strs.size() == 1 && str.empty(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') { + should_quote = true; + break; + } + } + if (should_quote) { + (*outptr ++) = '"'; + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == '\\' || c == '"') { + (*outptr ++) = '\\'; + (*outptr ++) = c; + } else if (c == '\n' || c == '\r') { + (*outptr ++) = '\\'; + (*outptr ++) = 'n'; + } else + (*outptr ++) = c; + } + (*outptr ++) = '"'; + } else { + memcpy(outptr, str.data(), str.size()); + outptr += str.size(); + } + } + return std::string(out.data(), outptr - out.data()); +} + +bool unescape_string_cstyle(const std::string &str, std::string &str_out) +{ + std::vector<char> out(str.size(), 0); + char *outptr = out.data(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == '\\') { + if (++ i == str.size()) + return false; + c = str[i]; + if (c == 'n') + (*outptr ++) = '\n'; + } else + (*outptr ++) = c; + } + str_out.assign(out.data(), outptr - out.data()); + return true; +} + +bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out) +{ + out.clear(); + if (str.empty()) + return true; + + size_t i = 0; + for (;;) { + // Skip white spaces. + char c = str[i]; + while (c == ' ' || c == '\t') { + if (++ i == str.size()) + return true; + c = str[i]; + } + // Start of a word. + std::vector<char> buf; + buf.reserve(16); + // Is it enclosed in quotes? + c = str[i]; + if (c == '"') { + // Complex case, string is enclosed in quotes. + for (++ i; i < str.size(); ++ i) { + c = str[i]; + if (c == '"') { + // End of string. + break; + } + if (c == '\\') { + if (++ i == str.size()) + return false; + c = str[i]; + if (c == 'n') + c = '\n'; + } + buf.push_back(c); + } + if (i == str.size()) + return false; + ++ i; + } else { + for (; i < str.size(); ++ i) { + c = str[i]; + if (c == ';') + break; + buf.push_back(c); + } + } + // Store the string into the output vector. + out.push_back(std::string(buf.data(), buf.size())); + if (i == str.size()) + break; + // Skip white spaces. + c = str[i]; + while (c == ' ' || c == '\t') { + if (++ i == str.size()) + // End of string. This is correct. + return true; + c = str[i]; + } + if (c != ';') + return false; + if (++ i == str.size()) { + // Emit one additional empty string. + out.push_back(std::string()); + return true; + } + } +} + bool operator== (const ConfigOption &a, const ConfigOption &b) { @@ -116,16 +269,16 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str) // Return an absolute value of a possibly relative config variable. // For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height. double -ConfigBase::get_abs_value(const t_config_option_key &opt_key) { - ConfigOption* opt = this->option(opt_key, false); - if (ConfigOptionFloatOrPercent* optv = dynamic_cast<ConfigOptionFloatOrPercent*>(opt)) { +ConfigBase::get_abs_value(const t_config_option_key &opt_key) const { + const ConfigOption* opt = this->option(opt_key); + if (const ConfigOptionFloatOrPercent* optv = dynamic_cast<const ConfigOptionFloatOrPercent*>(opt)) { // get option definition const ConfigOptionDef* def = this->def->get(opt_key); assert(def != NULL); // compute absolute value over the absolute value of the base option return optv->get_abs_value(this->get_abs_value(def->ratio_over)); - } else if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) { + } else if (const ConfigOptionFloat* optv = dynamic_cast<const ConfigOptionFloat*>(opt)) { return optv->value; } else { throw "Not a valid option type for get_abs_value()"; @@ -135,9 +288,9 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key) { // Return an absolute value of a possibly relative config variable. // For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value. double -ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) { +ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) const { // get stored option value - ConfigOptionFloatOrPercent* opt = dynamic_cast<ConfigOptionFloatOrPercent*>(this->option(opt_key)); + const ConfigOptionFloatOrPercent* opt = dynamic_cast<const ConfigOptionFloatOrPercent*>(this->option(opt_key)); assert(opt != NULL); // compute absolute value diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index ac6b03a49..3fbd82060 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -18,6 +18,11 @@ namespace Slic3r { typedef std::string t_config_option_key; typedef std::vector<std::string> t_config_option_keys; +extern std::string escape_string_cstyle(const std::string &str); +extern std::string escape_strings_cstyle(const std::vector<std::string> &strs); +extern bool unescape_string_cstyle(const std::string &str, std::string &out); +extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out); + // A generic value of a configuration option. class ConfigOption { public: @@ -112,6 +117,7 @@ class ConfigOptionFloats : public ConfigOptionVector<double> std::vector<std::string> vserialize() const { std::vector<std::string> vv; + vv.reserve(this->values.size()); for (std::vector<double>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { std::ostringstream ss; ss << *it; @@ -171,6 +177,7 @@ class ConfigOptionInts : public ConfigOptionVector<int> std::vector<std::string> vserialize() const { std::vector<std::string> vv; + vv.reserve(this->values.size()); for (std::vector<int>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { std::ostringstream ss; ss << *it; @@ -199,29 +206,12 @@ class ConfigOptionString : public ConfigOptionSingle<std::string> ConfigOptionString() : ConfigOptionSingle<std::string>("") {}; ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_value) {}; - std::string serialize() const { - std::string str = this->value; - - // s/\R/\\n/g - size_t pos = 0; - while ((pos = str.find("\n", pos)) != std::string::npos || (pos = str.find("\r", pos)) != std::string::npos) { - str.replace(pos, 1, "\\n"); - pos += 2; // length of "\\n" - } - - return str; - }; - + std::string serialize() const { + return escape_string_cstyle(this->value); + } + bool deserialize(std::string str) { - // s/\\n/\n/g - size_t pos = 0; - while ((pos = str.find("\\n", pos)) != std::string::npos) { - str.replace(pos, 2, "\n"); - pos += 1; // length of "\n" - } - - this->value = str; - return true; + return unescape_string_cstyle(str, this->value); }; }; @@ -231,12 +221,7 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string> public: std::string serialize() const { - std::ostringstream ss; - for (std::vector<std::string>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ";"; - ss << *it; - } - return ss.str(); + return escape_strings_cstyle(this->values); }; std::vector<std::string> vserialize() const { @@ -244,13 +229,7 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string> }; bool deserialize(std::string str) { - this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ';')) { - this->values.push_back(item_str); - } - return true; + return unescape_strings_cstyle(str, this->values); }; }; @@ -637,8 +616,8 @@ class ConfigBase std::string serialize(const t_config_option_key &opt_key) const; bool set_deserialize(const t_config_option_key &opt_key, std::string str); - double get_abs_value(const t_config_option_key &opt_key); - double get_abs_value(const t_config_option_key &opt_key, double ratio_over); + double get_abs_value(const t_config_option_key &opt_key) const; + double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; void setenv_(); }; diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index ffc10b9a4..ee76e75d9 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -865,6 +865,13 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed) this->config.max_volumetric_speed.value / path.mm3_per_mm ); } + if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { + // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) + speed = std::min( + speed, + EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm + ); + } double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 21a441a49..78b79d77d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -298,6 +298,31 @@ PrintConfigDef::PrintConfigDef() def->default_value = opt; } + def = this->add("filament_notes", coStrings); + def->label = "Filament notes"; + def->tooltip = "You can put your notes regarding the filament here."; + def->cli = "filament-notes=s@"; + def->multiline = true; + def->full_width = true; + def->height = 130; + { + ConfigOptionStrings* opt = new ConfigOptionStrings(); + opt->values.push_back(""); + def->default_value = opt; + } + + def = this->add("filament_max_volumetric_speed", coFloats); + def->label = "Max volumetric speed"; + def->tooltip = "Maximum volumetric speed allowed for this filament. Limits the maximum volumetric speed of a print to the minimum of print and filament volumetric speed. Set to zero for no limit."; + def->sidetext = "mm³/s"; + def->cli = "filament-max-volumetric-speed=f@"; + def->min = 0; + { + ConfigOptionFloats* opt = new ConfigOptionFloats(); + opt->values.push_back(0.f); + def->default_value = opt; + } + def = this->add("filament_diameter", coFloats); def->label = "Diameter"; def->tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 55d34368d..4efdd4173 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -292,6 +292,7 @@ class GCodeConfig : public virtual StaticPrintConfig ConfigOptionString extrusion_axis; ConfigOptionFloats extrusion_multiplier; ConfigOptionFloats filament_diameter; + ConfigOptionFloats filament_max_volumetric_speed; ConfigOptionBool gcode_comments; ConfigOptionEnum<GCodeFlavor> gcode_flavor; ConfigOptionString layer_gcode; @@ -326,6 +327,7 @@ class GCodeConfig : public virtual StaticPrintConfig OPT_PTR(extrusion_axis); OPT_PTR(extrusion_multiplier); OPT_PTR(filament_diameter); + OPT_PTR(filament_max_volumetric_speed); OPT_PTR(gcode_comments); OPT_PTR(gcode_flavor); OPT_PTR(layer_gcode); @@ -385,6 +387,7 @@ class PrintConfig : public GCodeConfig ConfigOptionBool fan_always_on; ConfigOptionInt fan_below_layer_time; ConfigOptionStrings filament_colour; + ConfigOptionStrings filament_notes; ConfigOptionFloat first_layer_acceleration; ConfigOptionInt first_layer_bed_temperature; ConfigOptionFloatOrPercent first_layer_extrusion_width; @@ -441,6 +444,7 @@ class PrintConfig : public GCodeConfig OPT_PTR(fan_always_on); OPT_PTR(fan_below_layer_time); OPT_PTR(filament_colour); + OPT_PTR(filament_notes); OPT_PTR(first_layer_acceleration); OPT_PTR(first_layer_bed_temperature); OPT_PTR(first_layer_extrusion_width); diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 838ce18b0..a4c5d5925 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,8 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 110; +use Test::More tests => 146; +use Data::Dumper; foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) { $config->set('layer_height', 0.3); @@ -24,7 +25,48 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo is $config->serialize('notes'), 'foo\nbar', 'serialize string with newline'; $config->set_deserialize('notes', 'bar\nbaz'); is $config->get('notes'), "bar\nbaz", 'deserialize string with newline'; - + + foreach my $test_data ( + { + name => 'empty', + values => [], + serialized => '' + }, + { + name => 'single empty', + values => [''], + serialized => '""' + }, + { + name => 'single noempty, simple', + values => ['RGB'], + serialized => 'RGB' + }, + { + name => 'multiple noempty, simple', + values => ['ABC', 'DEF', '09182745@!#$*(&'], + serialized => 'ABC;DEF;09182745@!#$*(&' + }, + { + name => 'multiple, simple, some empty', + values => ['ABC', 'DEF', '', '09182745@!#$*(&', ''], + serialized => 'ABC;DEF;;09182745@!#$*(&;' + }, + { + name => 'complex', + values => ['some "quoted" notes', "yet\n some notes", "whatever \n notes", ''], + serialized => '"some \"quoted\" notes";"yet\n some notes";"whatever \n notes";' + } + ) + { + $config->set('filament_notes', $test_data->{values}); + is $config->serialize('filament_notes'), $test_data->{serialized}, 'serialize multi-string value ' . $test_data->{name}; + $config->set_deserialize('filament_notes', ''); + is_deeply $config->get('filament_notes'), [], 'deserialize multi-string value - empty ' . $test_data->{name}; + $config->set_deserialize('filament_notes', $test_data->{serialized}); + is_deeply $config->get('filament_notes'), $test_data->{values}, 'deserialize complex multi-string value ' . $test_data->{name}; + } + $config->set('first_layer_height', 0.3); ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent'; is $config->serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent'; |