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.
This commit is contained in:
bubnikv 2016-11-01 13:41:24 +01:00
parent 4e66ed81d2
commit 3d3654707b
7 changed files with 280 additions and 46 deletions

View File

@ -138,7 +138,10 @@ sub new {
$self->_on_presets_changed;
});
# C++ instance DynamicPrintConfig
$self->{config} = Slic3r::Config->new;
# Initialize the DynamicPrintConfig by default keys/values.
# Possible %params keys: no_controller
$self->build(%params);
$self->update_tree;
$self->_update;
@ -910,7 +913,7 @@ sub build {
my $self = shift;
$self->init_config_options(qw(
filament_colour filament_diameter extrusion_multiplier
filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
fan_always_on cooling
min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers
@ -991,6 +994,27 @@ sub build {
$optgroup->append_single_option_line('min_print_speed');
}
}
{
my $page = $self->add_options_page('Advanced', 'wrench.png');
{
my $optgroup = $page->new_optgroup('Print speed override');
$optgroup->append_single_option_line('filament_max_volumetric_speed', 0);
}
}
{
my $page = $self->add_options_page('Notes', 'note.png');
{
my $optgroup = $page->new_optgroup('Notes',
label_width => 0,
);
my $option = $optgroup->get_option('filament_notes', 0);
$option->full_width(1);
$option->height(250);
$optgroup->append_single_option_line($option);
}
}
}
sub _update {

View File

@ -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

View File

@ -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;
@ -200,28 +207,11 @@ class ConfigOptionString : public 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 escape_string_cstyle(this->value);
}
return str;
};
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_();
};

View File

@ -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

View File

@ -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.";

View File

@ -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);

View File

@ -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);
@ -25,6 +26,47 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
$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';