Merge branch 'master' into wipe_tower_improvements

This commit is contained in:
bubnikv
2017-11-30 10:33:52 +01:00
95 changed files with 5177 additions and 2981 deletions

View File

@@ -7,7 +7,8 @@ namespace Slic3r {
template <class PointClass>
BoundingBoxBase<PointClass>::BoundingBoxBase(const std::vector<PointClass> &points)
{
if (points.empty()) CONFESS("Empty point set supplied to BoundingBoxBase constructor");
if (points.empty())
CONFESS("Empty point set supplied to BoundingBoxBase constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min.x = this->max.x = it->x;
this->min.y = this->max.y = it->y;
@@ -26,7 +27,8 @@ template <class PointClass>
BoundingBox3Base<PointClass>::BoundingBox3Base(const std::vector<PointClass> &points)
: BoundingBoxBase<PointClass>(points)
{
if (points.empty()) CONFESS("Empty point set supplied to BoundingBox3Base constructor");
if (points.empty())
CONFESS("Empty point set supplied to BoundingBox3Base constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min.z = this->max.z = it->z;
for (++it; it != points.end(); ++it) {
@@ -39,9 +41,10 @@ template BoundingBox3Base<Pointf3>::BoundingBox3Base(const std::vector<Pointf3>
BoundingBox::BoundingBox(const Lines &lines)
{
Points points;
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
points.push_back(line->a);
points.push_back(line->b);
points.reserve(lines.size());
for (const Line &line : lines) {
points.emplace_back(line.a);
points.emplace_back(line.b);
}
*this = BoundingBox(points);
}
@@ -190,9 +193,9 @@ BoundingBox3Base<PointClass>::size() const
}
template Pointf3 BoundingBox3Base<Pointf3>::size() const;
template <class PointClass> double
BoundingBoxBase<PointClass>::radius() const
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const
{
assert(this->defined);
double x = this->max.x - this->min.x;
double y = this->max.y - this->min.y;
return 0.5 * sqrt(x*x+y*y);
@@ -200,8 +203,7 @@ BoundingBoxBase<PointClass>::radius() const
template double BoundingBoxBase<Point>::radius() const;
template double BoundingBoxBase<Pointf>::radius() const;
template <class PointClass> double
BoundingBox3Base<PointClass>::radius() const
template <class PointClass> double BoundingBox3Base<PointClass>::radius() const
{
double x = this->max.x - this->min.x;
double y = this->max.y - this->min.y;

View File

@@ -1,6 +1,6 @@
#include "Config.hpp"
#include "Utils.hpp"
#include <assert.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <exception> // std::runtime_error
@@ -16,7 +16,6 @@
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <string.h>
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
@@ -235,10 +234,13 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
{
t_config_option_key opt_key = opt_key_src;
// Try to deserialize the option by its name.
const ConfigOptionDef* optdef = this->def()->get(opt_key);
const ConfigDef *def = this->def();
if (def == nullptr)
throw NoDefinitionException();
const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr) {
// If we didn't find an option, look for any other option having this as an alias.
for (const auto &opt : this->def()->options) {
for (const auto &opt : def->options) {
for (const t_config_option_key &opt_key2 : opt.second.aliases) {
if (opt_key2 == opt_key) {
opt_key = opt_key2;
@@ -278,10 +280,16 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
return static_cast<const ConfigOptionFloat*>(raw_opt)->value;
if (raw_opt->type() == coFloatOrPercent) {
// Get option definition.
const ConfigOptionDef *def = this->def()->get(opt_key);
assert(def != nullptr);
const ConfigDef *def = this->def();
if (def == nullptr)
throw NoDefinitionException();
const ConfigOptionDef *opt_def = def->get(opt_key);
assert(opt_def != nullptr);
// Compute absolute value over the absolute value of the base option.
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(def->ratio_over));
//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.
return opt_def->ratio_over.empty() ? 0. :
static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
}
throw std::runtime_error("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
}
@@ -321,11 +329,23 @@ void ConfigBase::setenv_()
void ConfigBase::load(const std::string &file)
{
namespace pt = boost::property_tree;
pt::ptree tree;
if (boost::iends_with(file, ".gcode") || boost::iends_with(file, ".g"))
this->load_from_gcode(file);
else
this->load_from_ini(file);
}
void ConfigBase::load_from_ini(const std::string &file)
{
boost::property_tree::ptree tree;
boost::nowide::ifstream ifs(file);
pt::read_ini(ifs, tree);
for (const pt::ptree::value_type &v : tree) {
boost::property_tree::read_ini(ifs, tree);
this->load(tree);
}
void ConfigBase::load(const boost::property_tree::ptree &tree)
{
for (const boost::property_tree::ptree::value_type &v : tree) {
try {
t_config_option_key opt_key = v.first;
this->set_deserialize(opt_key, v.second.get_value<std::string>());
@@ -414,18 +434,24 @@ void ConfigBase::save(const std::string &file) const
{
boost::nowide::ofstream c;
c.open(file, std::ios::out | std::ios::trunc);
{
std::time_t now;
time(&now);
char buf[sizeof "0000-00-00 00:00:00"];
strftime(buf, sizeof(buf), "%F %T", gmtime(&now));
c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << std::endl;
}
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
for (const std::string &opt_key : this->keys())
c << opt_key << " = " << this->serialize(opt_key) << std::endl;
c.close();
}
bool DynamicConfig::operator==(const DynamicConfig &rhs) const
{
t_options_map::const_iterator it1 = this->options.begin();
t_options_map::const_iterator it1_end = this->options.end();
t_options_map::const_iterator it2 = rhs.options.begin();
t_options_map::const_iterator it2_end = rhs.options.end();
for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2)
if (*it1->second != *it2->second)
return false;
return it1 == it1_end && it2 == it2_end;
}
ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create)
{
t_options_map::iterator it = options.find(opt_key);
@@ -436,7 +462,10 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// Option was not found and a new option shall not be created.
return nullptr;
// Try to create a new ConfigOption.
const ConfigOptionDef *optdef = this->def()->get(opt_key);
const ConfigDef *def = this->def();
if (def == nullptr)
throw NoDefinitionException();
const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr)
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
// Let the parent decide what to do if the opt_key is not defined by this->def().

View File

@@ -13,6 +13,8 @@
#include "libslic3r.h"
#include "Point.hpp"
#include <boost/property_tree/ptree.hpp>
namespace Slic3r {
// Name of the configuration option.
@@ -27,41 +29,42 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector<
// Type of a configuration value.
enum ConfigOptionType {
coNone,
coVectorType = 0x4000,
coNone = 0,
// single float
coFloat,
coFloat = 1,
// vector of floats
coFloats,
coFloats = coFloat + coVectorType,
// single int
coInt,
coInt = 2,
// vector of ints
coInts,
coInts = coInt + coVectorType,
// single string
coString,
coString = 3,
// vector of strings
coStrings,
coStrings = coString + coVectorType,
// percent value. Currently only used for infill.
coPercent,
coPercent = 4,
// percents value. Currently used for retract before wipe only.
coPercents,
coPercents = coPercent + coVectorType,
// a fraction or an absolute value
coFloatOrPercent,
coFloatOrPercent = 5,
// single 2d point. Currently not used.
coPoint,
coPoint = 6,
// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
coPoints,
coPoints = coPoint + coVectorType,
// single boolean value
coBool,
coBool = 7,
// vector of boolean values
coBools,
coBools = coBool + coVectorType,
// a generic enum
coEnum,
coEnum = 8,
};
// A generic value of a configuration option.
class ConfigOption {
public:
virtual ~ConfigOption() {};
virtual ~ConfigOption() {}
virtual ConfigOptionType type() const = 0;
virtual std::string serialize() const = 0;
@@ -75,8 +78,13 @@ public:
virtual void setInt(int /* val */) { throw std::runtime_error("Calling ConfigOption::setInt on a non-int ConfigOption"); }
virtual bool operator==(const ConfigOption &rhs) const = 0;
bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
bool is_vector() const { return ! this->is_scalar(); }
};
typedef ConfigOption* ConfigOptionPtr;
typedef const ConfigOption* ConfigOptionConstPtr;
// Value of a single valued option (bool, int, float, string, point, enum)
template <class T>
class ConfigOptionSingle : public ConfigOption {
@@ -91,7 +99,7 @@ public:
throw std::runtime_error("ConfigOptionSingle: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
};
}
bool operator==(const ConfigOption &rhs) const override
{
@@ -110,6 +118,25 @@ class ConfigOptionVectorBase : public ConfigOption {
public:
// Currently used only to initialize the PlaceholderParser.
virtual std::vector<std::string> vserialize() const = 0;
// Set from a vector of ConfigOptions.
// If the rhs ConfigOption is scalar, then its value is used,
// otherwise for each of rhs, the first value of a vector is used.
// This function is useful to collect values for multiple extrder / filament settings.
virtual void set(const std::vector<const ConfigOption*> &rhs) = 0;
// Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
// This function is useful to split values from multiple extrder / filament settings into separate configurations.
virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0;
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
// Get size of this vector.
virtual size_t size() const = 0;
// Is this vector empty?
virtual bool empty() const = 0;
protected:
// Used to verify type compatibility when assigning to / from a scalar ConfigOption.
ConfigOptionType scalar_type() const { return static_cast<ConfigOptionType>(this->type() - coVectorType); }
};
// Value of a vector valued option (bools, ints, floats, strings, points), template
@@ -117,6 +144,11 @@ template <class T>
class ConfigOptionVector : public ConfigOptionVectorBase
{
public:
ConfigOptionVector() {}
explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {}
explicit ConfigOptionVector(std::initializer_list<T> il) : values(std::move(il)) {}
explicit ConfigOptionVector(const std::vector<T> &values) : values(values) {}
explicit ConfigOptionVector(std::vector<T> &&values) : values(std::move(values)) {}
std::vector<T> values;
void set(const ConfigOption *rhs) override
@@ -125,16 +157,88 @@ public:
throw std::runtime_error("ConfigOptionVector: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
};
}
// Set from a vector of ConfigOptions.
// If the rhs ConfigOption is scalar, then its value is used,
// otherwise for each of rhs, the first value of a vector is used.
// This function is useful to collect values for multiple extrder / filament settings.
void set(const std::vector<const ConfigOption*> &rhs) override
{
this->values.clear();
this->values.reserve(rhs.size());
for (const ConfigOption *opt : rhs) {
if (opt->type() == this->type()) {
auto other = static_cast<const ConfigOptionVector<T>*>(opt);
if (other->values.empty())
throw std::runtime_error("ConfigOptionVector::set(): Assigning from an empty vector");
this->values.emplace_back(other->values.front());
} else if (opt->type() == this->scalar_type())
this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
else
throw std::runtime_error("ConfigOptionVector::set():: Assigning an incompatible type");
}
}
// Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
// This function is useful to split values from multiple extrder / filament settings into separate configurations.
void set_at(const ConfigOption *rhs, size_t i, size_t j) override
{
// It is expected that the vector value has at least one value, which is the default, if not overwritten.
assert(! this->values.empty());
if (this->values.size() <= i) {
// Resize this vector, fill in the new vector fields with the copy of the first field.
T v = this->values.front();
this->values.resize(i + 1, v);
}
if (rhs->type() == this->type()) {
// Assign the first value of the rhs vector.
auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
if (other->values.empty())
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning from an empty vector");
this->values[i] = other->get_at(j);
} else if (rhs->type() == this->scalar_type())
this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
else
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning an incompatible type");
}
T& get_at(size_t i)
{
assert(! this->values.empty());
return (i < this->values.size()) ? this->values[i] : this->values.front();
};
}
const T& get_at(size_t i) const { return const_cast<ConfigOptionVector<T>*>(this)->get_at(i); }
// Resize this vector by duplicating the last value.
// If the current vector is empty, the default value is used instead.
void resize(size_t n, const ConfigOption *opt_default = nullptr) override
{
assert(opt_default == nullptr || opt_default->is_vector());
// assert(opt_default == nullptr || dynamic_cast<ConfigOptionVector<T>>(opt_default));
assert(! this->values.empty() || opt_default != nullptr);
if (n == 0)
this->values.clear();
else if (n < this->values.size())
this->values.erase(this->values.begin() + n, this->values.end());
else if (n > this->values.size()) {
if (this->values.empty()) {
if (opt_default == nullptr)
throw std::runtime_error("ConfigOptionVector::resize(): No default value provided.");
if (opt_default->type() != this->type())
throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type.");
this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front());
} else {
// Resize by duplicating the last value.
this->values.resize(n, this->values.back());
}
}
}
size_t size() const override { return this->values.size(); }
bool empty() const override { return this->values.empty(); }
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
@@ -150,13 +254,14 @@ public:
class ConfigOptionFloat : public ConfigOptionSingle<double>
{
public:
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {};
explicit ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {};
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {}
explicit ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {}
ConfigOptionType type() const override { return coFloat; }
double getFloat() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionFloat(*this); }
bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.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 { return this->value == rhs.value; }
std::string serialize() const override
{
@@ -183,9 +288,14 @@ public:
class ConfigOptionFloats : public ConfigOptionVector<double>
{
public:
ConfigOptionType type() const override { return coFloats; }
ConfigOption* clone() const override { return new ConfigOptionFloats(*this); }
bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; }
ConfigOptionFloats() : ConfigOptionVector<double>() {}
explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
explicit ConfigOptionFloats(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
static ConfigOptionType static_type() { return coFloats; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloats(*this); }
bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; }
std::string serialize() const override
{
@@ -195,7 +305,7 @@ public:
ss << *it;
}
return ss.str();
};
}
std::vector<std::string> vserialize() const override
{
@@ -234,15 +344,16 @@ public:
class ConfigOptionInt : public ConfigOptionSingle<int>
{
public:
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
explicit ConfigOptionInt(int value) : ConfigOptionSingle<int>(value) {};
explicit ConfigOptionInt(double _value) : ConfigOptionSingle<int>(int(floor(_value + 0.5))) {};
ConfigOptionInt() : ConfigOptionSingle<int>(0) {}
explicit ConfigOptionInt(int value) : ConfigOptionSingle<int>(value) {}
explicit ConfigOptionInt(double _value) : ConfigOptionSingle<int>(int(floor(_value + 0.5))) {}
ConfigOptionType type() const override { return coInt; }
int getInt() const override { return this->value; };
void setInt(int val) { this->value = val; };
ConfigOption* clone() const override { return new ConfigOptionInt(*this); }
bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; }
static ConfigOptionType static_type() { return coInt; }
ConfigOptionType type() const override { return static_type(); }
int getInt() const override { return this->value; }
void setInt(int val) { this->value = val; }
ConfigOption* clone() const override { return new ConfigOptionInt(*this); }
bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; }
std::string serialize() const override
{
@@ -269,10 +380,15 @@ public:
class ConfigOptionInts : public ConfigOptionVector<int>
{
public:
ConfigOptionType type() const override { return coInts; }
ConfigOption* clone() const override { return new ConfigOptionInts(*this); }
ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionInts &rhs) const { return this->values == rhs.values; }
ConfigOptionInts() : ConfigOptionVector<int>() {}
explicit ConfigOptionInts(size_t n, int value) : ConfigOptionVector<int>(n, value) {}
explicit ConfigOptionInts(std::initializer_list<int> il) : ConfigOptionVector<int>(std::move(il)) {}
static ConfigOptionType static_type() { return coInts; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionInts(*this); }
ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionInts &rhs) const { return this->values == rhs.values; }
std::string serialize() const override {
std::ostringstream ss;
@@ -314,13 +430,14 @@ public:
class ConfigOptionString : public ConfigOptionSingle<std::string>
{
public:
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
explicit ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_value) {};
ConfigOptionString() : ConfigOptionSingle<std::string>("") {}
explicit ConfigOptionString(const std::string &value) : ConfigOptionSingle<std::string>(value) {}
ConfigOptionType type() const override { return coString; }
ConfigOption* clone() const override { return new ConfigOptionString(*this); }
ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; }
static ConfigOptionType static_type() { return coString; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionString(*this); }
ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; }
std::string serialize() const override
{
@@ -338,10 +455,17 @@ public:
class ConfigOptionStrings : public ConfigOptionVector<std::string>
{
public:
ConfigOptionType type() const override { return coStrings; }
ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; }
ConfigOptionStrings() : ConfigOptionVector<std::string>() {}
explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector<std::string>(n, value) {}
explicit ConfigOptionStrings(const std::vector<std::string> &values) : ConfigOptionVector<std::string>(values) {}
explicit ConfigOptionStrings(std::vector<std::string> &&values) : ConfigOptionVector<std::string>(std::move(values)) {}
explicit ConfigOptionStrings(std::initializer_list<std::string> il) : ConfigOptionVector<std::string>(std::move(il)) {}
static ConfigOptionType static_type() { return coStrings; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; }
std::string serialize() const override
{
@@ -364,14 +488,15 @@ public:
class ConfigOptionPercent : public ConfigOptionFloat
{
public:
ConfigOptionPercent() : ConfigOptionFloat(0) {};
explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
ConfigOptionPercent() : ConfigOptionFloat(0) {}
explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {}
ConfigOptionType type() const override { return coPercent; }
ConfigOption* clone() const override { return new ConfigOptionPercent(*this); }
ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; }
double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; }
static ConfigOptionType static_type() { return coPercent; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPercent(*this); }
ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; }
double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; }
std::string serialize() const override
{
@@ -395,10 +520,15 @@ public:
class ConfigOptionPercents : public ConfigOptionFloats
{
public:
ConfigOptionType type() const override { return coPercents; }
ConfigOption* clone() const override { return new ConfigOptionPercents(*this); }
ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercents &rhs) const { return this->values == rhs.values; }
ConfigOptionPercents() : ConfigOptionFloats() {}
explicit ConfigOptionPercents(size_t n, double value) : ConfigOptionFloats(n, value) {}
explicit ConfigOptionPercents(std::initializer_list<double> il) : ConfigOptionFloats(std::move(il)) {}
static ConfigOptionType static_type() { return coPercents; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPercents(*this); }
ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercents &rhs) const { return this->values == rhs.values; }
std::string serialize() const override
{
@@ -445,10 +575,11 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
{
public:
bool percent;
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {};
explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {};
ConfigOptionType type() const override { return coFloatOrPercent; }
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {}
explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {}
static ConfigOptionType static_type() { return coFloatOrPercent; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); }
ConfigOptionFloatOrPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionFloatOrPercent &rhs) const
@@ -485,13 +616,14 @@ public:
class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
{
public:
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {};
explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle<Pointf>(value) {};
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {}
explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle<Pointf>(value) {}
ConfigOptionType type() const override { return coPoint; }
ConfigOption* clone() const override { return new ConfigOptionPoint(*this); }
ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; }
static ConfigOptionType static_type() { return coPoint; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoint(*this); }
ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; }
std::string serialize() const override
{
@@ -517,10 +649,15 @@ public:
class ConfigOptionPoints : public ConfigOptionVector<Pointf>
{
public:
ConfigOptionType type() const override { return coPoints; }
ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; }
ConfigOptionPoints() : ConfigOptionVector<Pointf>() {}
explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector<Pointf>(n, value) {}
explicit ConfigOptionPoints(std::initializer_list<Pointf> il) : ConfigOptionVector<Pointf>(std::move(il)) {}
static ConfigOptionType static_type() { return coPoints; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; }
std::string serialize() const override
{
@@ -570,14 +707,15 @@ public:
class ConfigOptionBool : public ConfigOptionSingle<bool>
{
public:
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {};
explicit ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {};
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {}
explicit ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {}
ConfigOptionType type() const override { return coBool; }
bool getBool() const override { return this->value; };
ConfigOption* clone() const override { return new ConfigOptionBool(*this); }
ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; }
static ConfigOptionType static_type() { return coBool; }
ConfigOptionType type() const override { return static_type(); }
bool getBool() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionBool(*this); }
ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; }
std::string serialize() const override
{
@@ -595,10 +733,15 @@ public:
class ConfigOptionBools : public ConfigOptionVector<unsigned char>
{
public:
ConfigOptionType type() const override { return coBools; }
ConfigOption* clone() const override { return new ConfigOptionBools(*this); }
ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBools &rhs) const { return this->values == rhs.values; }
ConfigOptionBools() : ConfigOptionVector<unsigned char>() {}
explicit ConfigOptionBools(size_t n, bool value) : ConfigOptionVector<unsigned char>(n, (unsigned char)value) {}
explicit ConfigOptionBools(std::initializer_list<bool> il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); }
static ConfigOptionType static_type() { return coBools; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionBools(*this); }
ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBools &rhs) const { return this->values == rhs.values; }
bool& get_at(size_t i) {
assert(! this->values.empty());
@@ -652,13 +795,14 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
{
public:
// by default, use the first value (0) of the T enum type
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {};
explicit ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {};
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {}
explicit ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {}
ConfigOptionType type() const override { return coEnum; }
ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); }
ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnum<T> &rhs) const { return this->value == rhs.value; }
static ConfigOptionType static_type() { return coEnum; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); }
ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnum<T> &rhs) const { return this->value == rhs.value; }
std::string serialize() const override
{
@@ -717,8 +861,9 @@ public:
const t_config_enum_values* keys_map;
ConfigOptionType type() const override { return coEnum; }
ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); }
static ConfigOptionType static_type() { return coEnum; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); }
ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; }
@@ -874,6 +1019,16 @@ public:
{ return const_cast<ConfigBase*>(this)->option(opt_key, false); }
ConfigOption* option(const t_config_option_key &opt_key, bool create = false)
{ return this->optptr(opt_key, create); }
template<typename TYPE>
TYPE* option(const t_config_option_key &opt_key, bool create = false)
{
ConfigOption *opt = this->optptr(opt_key, create);
assert(opt == nullptr || opt->type() == TYPE::static_type());
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
}
template<typename TYPE>
const TYPE* option(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option<TYPE>(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.
@@ -893,7 +1048,9 @@ public:
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
void setenv_();
void load(const std::string &file);
void load_from_ini(const std::string &file);
void load_from_gcode(const std::string &file);
void load(const boost::property_tree::ptree &tree);
void save(const std::string &file) const;
private:
@@ -906,26 +1063,77 @@ private:
class DynamicConfig : public virtual ConfigBase
{
public:
DynamicConfig() {}
DynamicConfig(const DynamicConfig& other) { *this = other; }
DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); }
virtual ~DynamicConfig() { clear(); }
DynamicConfig& operator=(const DynamicConfig &other)
// Copy a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
DynamicConfig& operator=(const DynamicConfig &rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
for (const auto &kvp : other.options)
for (const auto &kvp : rhs.options)
this->options[kvp.first] = kvp.second->clone();
return *this;
}
DynamicConfig& operator=(DynamicConfig &&other)
// Move a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
DynamicConfig& operator=(DynamicConfig &&rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
this->options = std::move(other.options);
other.options.clear();
this->options = std::move(rhs.options);
rhs.options.clear();
return *this;
}
// Add a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
DynamicConfig& operator+=(const DynamicConfig &rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
for (const auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end())
this->options[kvp.first] = kvp.second->clone();
else {
assert(it->second->type() == kvp.second->type());
if (it->second->type() == kvp.second->type())
*it->second = *kvp.second;
else {
delete it->second;
it->second = kvp.second->clone();
}
}
}
return *this;
}
// Move a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
DynamicConfig& operator+=(DynamicConfig &&rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
for (const auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end()) {
this->options[kvp.first] = kvp.second;
} else {
assert(it->second->type() == kvp.second->type());
delete it->second;
it->second = kvp.second;
}
}
rhs.options.clear();
return *this;
}
bool operator==(const DynamicConfig &rhs) const;
bool operator!=(const DynamicConfig &rhs) const { return ! (*this == rhs); }
void swap(DynamicConfig &other)
{
std::swap(this->options, other.options);
@@ -948,30 +1156,49 @@ public:
return true;
}
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
// Allow DynamicConfig to be instantiated on ints own without a definition.
// If the definition is not defined, the method requiring the definition will throw NoDefinitionException.
const ConfigDef* def() const override { return nullptr; };
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
// 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.
t_config_option_keys keys() const override;
std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return dynamic_cast<ConfigOptionString*>(this->option(opt_key, create))->value; }
// Set a value for an opt_key. Returns true if the value did not exist yet.
// This DynamicConfig will take ownership of opt.
// Be careful, as this method does not test the existence of opt_key in this->def().
bool set_key_value(const std::string &opt_key, ConfigOption *opt)
{
auto it = this->options.find(opt_key);
if (it == this->options.end()) {
this->options[opt_key] = opt;
return true;
} else {
delete it->second;
it->second = opt;
return false;
}
}
std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option<ConfigOptionString>(opt_key, create)->value; }
const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key); }
std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast<ConfigOptionStrings*>(this->option(opt_key))->get_at(idx); }
std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionStrings>(opt_key)->get_at(idx); }
const std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key, idx); }
double& opt_float(const t_config_option_key &opt_key) { return dynamic_cast<ConfigOptionFloat*>(this->option(opt_key))->value; }
double& opt_float(const t_config_option_key &opt_key) { return this->option<ConfigOptionFloat>(opt_key)->value; }
const double opt_float(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionFloat*>(this->option(opt_key))->value; }
double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast<ConfigOptionFloats*>(this->option(opt_key))->get_at(idx); }
double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloats>(opt_key)->get_at(idx); }
const double opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloats*>(this->option(opt_key))->get_at(idx); }
int& opt_int(const t_config_option_key &opt_key) { return dynamic_cast<ConfigOptionInt*>(this->option(opt_key))->value; }
int& opt_int(const t_config_option_key &opt_key) { return this->option<ConfigOptionInt>(opt_key)->value; }
const int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionInt*>(this->option(opt_key))->value; }
int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast<ConfigOptionInts*>(this->option(opt_key))->get_at(idx); }
int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionInts>(opt_key)->get_at(idx); }
const int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(this->option(opt_key))->get_at(idx); }
protected:
DynamicConfig() {}
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
@@ -1001,6 +1228,13 @@ public:
const char* what() const noexcept override { return "Unknown config option"; }
};
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
class NoDefinitionException : public std::exception
{
public:
const char* what() const noexcept override { return "No config definition"; }
};
}
#endif

View File

@@ -149,7 +149,6 @@ inline Polylines to_polylines(const ExPolygons &src)
return polylines;
}
#if SLIC3R_CPPVER >= 11
inline Polylines to_polylines(ExPolygon &&src)
{
Polylines polylines;
@@ -166,6 +165,7 @@ inline Polylines to_polylines(ExPolygon &&src)
assert(idx == polylines.size());
return polylines;
}
inline Polylines to_polylines(ExPolygons &&src)
{
Polylines polylines;
@@ -184,7 +184,6 @@ inline Polylines to_polylines(ExPolygons &&src)
assert(idx == polylines.size());
return polylines;
}
#endif
inline Polygons to_polygons(const ExPolygon &src)
{

View File

@@ -149,8 +149,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// );
}
for (Surfaces::const_iterator surface_it = surfaces.begin(); surface_it != surfaces.end(); ++ surface_it) {
const Surface &surface = *surface_it;
for (const Surface &surface : surfaces) {
if (surface.surface_type == stInternalVoid)
continue;
InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value;
@@ -262,10 +261,10 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Unpacks the collection, creates multiple collections per path.
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
// Why the paths are unpacked?
for (ExtrusionEntitiesPtr::iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) {
for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) {
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
out.entities.push_back(&collection);
collection.entities.push_back((*thin_fill)->clone());
collection.entities.push_back(thin_fill->clone());
}
}

View File

@@ -176,11 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single(
}
}
Polylines chained = PolylineCollection::chained_path_from(
#if SLIC3R_CPPVER >= 11
std::move(polylines),
#else
polylines,
#endif
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
@@ -199,12 +195,7 @@ void Fill3DHoneycomb::_fill_surface_single(
}
}
// The lines cannot be connected.
#if SLIC3R_CPPVER >= 11
polylines_out.push_back(std::move(*it_polyline));
#else
polylines_out.push_back(Polyline());
std::swap(polylines_out.back(), *it_polyline);
#endif
polylines_out.emplace_back(std::move(*it_polyline));
first = false;
}
}

View File

@@ -17,12 +17,7 @@ void FillHoneycomb::_fill_surface_single(
CacheID cache_id(params.density, this->spacing);
Cache::iterator it_m = this->cache.find(cache_id);
if (it_m == this->cache.end()) {
#if 0
// #if SLIC3R_CPPVER > 11
it_m = this->cache.emplace_hint(it_m);
#else
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
#endif
CacheData &m = it_m->second;
coord_t min_spacing = scale_(this->spacing);
m.distance = min_spacing / params.density;
@@ -99,11 +94,7 @@ void FillHoneycomb::_fill_surface_single(
// connect paths
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
Polylines chained = PolylineCollection::chained_path_from(
#if SLIC3R_CPPVER >= 11
std::move(paths),
#else
paths,
#endif
PolylineCollection::leftmost_point(paths), false);
assert(paths.empty());
paths.clear();

View File

@@ -93,11 +93,7 @@ void FillRectilinear::_fill_surface_single(
}
}
Polylines chained = PolylineCollection::chained_path_from(
#if SLIC3R_CPPVER >= 11
std::move(polylines),
#else
polylines,
#endif
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
@@ -118,12 +114,7 @@ void FillRectilinear::_fill_surface_single(
}
}
// The lines cannot be connected.
#if SLIC3R_CPPVER >= 11
polylines_out.push_back(std::move(*it_polyline));
#else
polylines_out.push_back(Polyline());
std::swap(polylines_out.back(), *it_polyline);
#endif
polylines_out.emplace_back(std::move(*it_polyline));
first = false;
}
}

View File

@@ -8,39 +8,6 @@ namespace Slic3r {
// This static method returns a sane extrusion width default.
static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, float height)
{
#if 0
// Here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate.
// shape: rectangle with semicircles at the ends
// This "sane" extrusion width gives the following results for a 0.4mm dmr nozzle:
// Layer Calculated Calculated width
// heigh extrusion over nozzle
// width diameter
// 0.40 0.40 1.00
// 0.35 0.43 1.09
// 0.30 0.48 1.21
// 0.25 0.56 1.39
// 0.20 0.67 1.68
// 0.15 0.87 2.17
// 0.10 1.28 3.20
// 0.05 2.52 6.31
//
float width = float(0.25 * (nozzle_diameter * nozzle_diameter) * PI / height + height * (1.0 - 0.25 * PI));
switch (role) {
case frExternalPerimeter:
case frSupportMaterial:
case frSupportMaterialInterface:
return nozzle_diameter;
case frPerimeter:
case frSolidInfill:
case frTopSolidInfill:
// do not limit width for sparse infill so that we use full native flow for it
return std::min(std::max(width, nozzle_diameter * 1.05f), nozzle_diameter * 1.7f);
case frInfill:
default:
return std::max(width, nozzle_diameter * 1.05f);
}
#else
switch (role) {
case frSupportMaterial:
case frSupportMaterialInterface:
@@ -53,7 +20,6 @@ static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, f
case frInfill:
return 1.125f * nozzle_diameter;
}
#endif
}
// This constructor builds a Flow object from an extrusion width config setting
@@ -154,10 +120,11 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{
const auto &width = (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width;
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width,
(width.value > 0) ? width : object->config.extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
false);

View File

@@ -52,6 +52,9 @@ public:
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); };
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
// Create a flow from the spacing of extrusion lines.
// 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);
};

View File

@@ -4,6 +4,7 @@
#include "Geometry.hpp"
#include "GCode/PrintExtents.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include "Utils.hpp"
#include <algorithm>
#include <cstdlib>
@@ -11,7 +12,6 @@
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/find.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/foreach.hpp>
#include <boost/nowide/iostream.hpp>
@@ -264,11 +264,14 @@ inline void write(FILE *file, const std::string &what)
fwrite(what.data(), 1, what.size(), file);
}
// Write a string into a file. Add a newline, if the string does not end with a newline already.
// Used to export a custom G-code section processed by the PlaceholderParser.
inline void writeln(FILE *file, const std::string &what)
{
if (! what.empty()) {
write(file, what);
fprintf(file, "\n");
if (what.back() != '\n')
fprintf(file, "\n");
}
}
@@ -462,15 +465,7 @@ bool GCode::_do_export(Print &print, FILE *file)
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
// Write information on the generator.
{
const auto now = boost::posix_time::second_clock::local_time();
const auto date = now.date();
fprintf(file, "; generated by Slic3r %s on %04d-%02d-%02d at %02d:%02d:%02d\n\n",
SLIC3R_VERSION,
// Local date in an ANSII format.
int(now.date().year()), int(now.date().month()), int(now.date().day()),
int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds()));
}
fprintf(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
// Write notes (content of the Print Settings tab -> Notes)
{
std::list<std::string> lines;
@@ -488,6 +483,7 @@ bool GCode::_do_export(Print &print, FILE *file)
{
const PrintObject *first_object = print.objects.front();
const double layer_height = first_object->config.layer_height.value;
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
auto region = print.regions[region_id];
fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
@@ -498,7 +494,7 @@ bool GCode::_do_export(Print &print, FILE *file)
if (print.has_support_material())
fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
if (print.config.first_layer_extrusion_width.value > 0)
fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, true, -1., *first_object).width);
fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
fprintf(file, "\n");
}
}
@@ -542,29 +538,25 @@ bool GCode::_do_export(Print &print, FILE *file)
// Disable fan.
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
write(file, m_writer.set_fan(0, true));
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
{
// Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if
// the custom start G-code emited these.
//FIXME Should one parse the custom G-code to initialize the "current" bed temp state at m_writer?
std::string gcode = m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.get_at(initial_extruder_id), true);
if (boost::ifind_first(print.config.start_gcode.value, std::string("M140")).empty() &&
boost::ifind_first(print.config.start_gcode.value, std::string("M190")).empty())
write(file, gcode);
}
// Set extruder(s) temperature before and after start G-code.
this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, false);
// Let the start-up script prime the 1st printing tool.
m_placeholder_parser.set("initial_tool", initial_extruder_id);
m_placeholder_parser.set("initial_extruder", initial_extruder_id);
m_placeholder_parser.set("current_extruder", initial_extruder_id);
writeln(file, m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id));
// Useful for sequential prints.
m_placeholder_parser.set("current_object_idx", 0);
std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id);
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
// Set extruder(s) temperature before and after start G-code.
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
// Write the custom start G-code
writeln(file, start_gcode);
// Process filament-specific gcode in extruder order.
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, true);
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
// Set other general things.
write(file, this->preamble());
@@ -654,9 +646,12 @@ bool GCode::_do_export(Print &print, FILE *file)
// Ff we are printing the bottom layer of an object, and we have already finished
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
write(file, m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.get_at(initial_extruder_id)));
// Set first layer extruder.
this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, false);
m_placeholder_parser.set("current_object_idx", int(finished_objects));
std::string between_objects_gcode = m_placeholder_parser.process(print.config.between_objects_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
writeln(file, between_objects_gcode);
}
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
m_cooling_buffer->reset();
@@ -778,15 +773,96 @@ bool GCode::_do_export(Print &print, FILE *file)
return true;
}
// Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait inside the custom G-code.
// Returns true if one of the temp commands are found, and try to parse the target temperature value into temp_out.
static bool custom_gcode_sets_temperature(const std::string &gcode, const int mcode_set_temp_dont_wait, const int mcode_set_temp_and_wait, int &temp_out)
{
temp_out = -1;
if (gcode.empty())
return false;
const char *ptr = gcode.data();
bool temp_set_by_gcode = false;
while (*ptr != 0) {
// Skip whitespaces.
for (; *ptr == ' ' || *ptr == '\t'; ++ ptr);
if (*ptr == 'M') {
// Line starts with 'M'. It is a machine command.
++ ptr;
// Parse the M code value.
char *endptr = nullptr;
int mcode = int(strtol(ptr, &endptr, 10));
if (endptr != nullptr && endptr != ptr && (mcode == mcode_set_temp_dont_wait || mcode == mcode_set_temp_and_wait)) {
// M104/M109 or M140/M190 found.
ptr = endptr;
// Let the caller know that the custom G-code sets the temperature.
temp_set_by_gcode = true;
// Now try to parse the temperature value.
// While not at the end of the line:
while (strchr(";\r\n\0", *ptr) == nullptr) {
// Skip whitespaces.
for (; *ptr == ' ' || *ptr == '\t'; ++ ptr);
if (*ptr == 'S') {
// Skip whitespaces.
for (++ ptr; *ptr == ' ' || *ptr == '\t'; ++ ptr);
// Parse an int.
endptr = nullptr;
long temp_parsed = strtol(ptr, &endptr, 10);
if (endptr > ptr) {
ptr = endptr;
temp_out = temp_parsed;
}
} else {
// Skip this word.
for (; strchr(" \t;\r\n\0", *ptr) == nullptr; ++ ptr);
}
}
}
}
// Skip the rest of the line.
for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr);
// Skip the end of line indicators.
for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
}
return temp_set_by_gcode;
}
// Write 1st layer bed temperatures into the G-code.
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M140 - Set Extruder Temperature
// M190 - Set Extruder Temperature and Wait
void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
// Initial bed temperature based on the first extruder.
int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id);
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode);
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
temp = temp_by_gcode;
// Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if
// the custom start G-code emited these.
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
if (! temp_by_gcode)
write(file, set_temp_gcode);
}
// Write 1st layer extruder temperatures into the G-code.
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// FIXME this does not work correctly for multi-extruder, single heater configuration as it emits multiple preheat commands for the same heater.
// M104 - Set Extruder Temperature
// M109 - Set Extruder Temperature and Wait
void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, unsigned int first_printing_extruder_id, bool wait)
void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
if (boost::ifind_first(print.config.start_gcode.value, std::string("M104")).empty() &&
boost::ifind_first(print.config.start_gcode.value, std::string("M109")).empty()) {
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) {
// Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code.
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
temp = temp_by_gcode;
m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id);
} else {
// Custom G-code does not set the extruder temperature. Do it now.
if (print.config.single_extruder_multi_material.value) {
// Set temperature of the first printing extruder only.
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
@@ -894,18 +970,22 @@ void GCode::process_layer(
// Set new layer - this will change Z and force a retraction if retract_layer_change is enabled.
if (! print.config.before_layer_gcode.value.empty()) {
PlaceholderParser pp(m_placeholder_parser);
pp.set("layer_num", m_layer_index + 1);
pp.set("layer_z", print_z);
gcode += pp.process(print.config.before_layer_gcode.value, m_writer.extruder()->id()) + "\n";
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += m_placeholder_parser.process(
print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
gcode += this->change_layer(print_z); // this will increase m_layer_index
m_layer = &layer;
if (! print.config.layer_gcode.value.empty()) {
PlaceholderParser pp(m_placeholder_parser);
pp.set("layer_num", m_layer_index);
pp.set("layer_z", print_z);
gcode += pp.process(print.config.layer_gcode.value, m_writer.extruder()->id()) + "\n";
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += m_placeholder_parser.process(
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
if (! first_layer && ! m_second_layer_things_done) {
@@ -2098,10 +2178,12 @@ std::string GCode::set_extruder(unsigned int extruder_id)
// append custom toolchange G-code
if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) {
PlaceholderParser pp = m_placeholder_parser;
pp.set("previous_extruder", m_writer.extruder()->id());
pp.set("next_extruder", extruder_id);
gcode += pp.process(m_config.toolchange_gcode.value, extruder_id) + '\n';
DynamicConfig config;
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
gcode += m_placeholder_parser.process(
m_config.toolchange_gcode.value, extruder_id, &config)
+ '\n';
}
// if ooze prevention is enabled, park current extruder in the nearest

View File

@@ -268,7 +268,8 @@ protected:
std::pair<const PrintObject*, Point> m_last_obj_copy;
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
// this flag triggers first layer speeds
bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }

View File

@@ -21,8 +21,8 @@ public:
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
bool operator==(const xy &rhs) { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) { return x != rhs.x || y != rhs.y; }
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
// Rotate the point around given point about given angle (in degrees)
xy rotate(const xy& origin, float angle) const {

View File

@@ -21,6 +21,35 @@
#endif
#endif
#include <boost/algorithm/string.hpp>
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/config/warning_disable.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/repository/include/qi_distinct.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/phoenix/bind/bind_function.hpp>
#include <iostream>
#include <string>
namespace Slic3r {
PlaceholderParser::PlaceholderParser()
@@ -61,130 +90,821 @@ void PlaceholderParser::update_timestamp()
// are expected to be addressed by the extruder ID, therefore
// if a vector configuration value is addressed without an index,
// a current extruder ID is used.
void PlaceholderParser::apply_config(const DynamicPrintConfig &config)
void PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
{
for (const t_config_option_key &opt_key : config.keys()) {
const ConfigOptionDef* def = config.def()->get(opt_key);
if (def->multiline || opt_key == "post_process")
const ConfigDef *def = rhs.def();
for (const t_config_option_key &opt_key : rhs.keys()) {
const ConfigOptionDef *opt_def = def->get(opt_key);
if ((opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process")
continue;
const ConfigOption* opt = config.option(opt_key);
const ConfigOptionVectorBase* optv = dynamic_cast<const ConfigOptionVectorBase*>(opt);
if (optv != nullptr && opt_key != "bed_shape") {
// set placeholders for options with multiple values
this->set(opt_key, optv->vserialize());
} else if (const ConfigOptionPoint* optp = dynamic_cast<const ConfigOptionPoint*>(opt)) {
this->set(opt_key, optp->serialize());
Pointf val = *optp;
this->set(opt_key + "_X", val.x);
this->set(opt_key + "_Y", val.y);
} else {
// set single-value placeholders
this->set(opt_key, opt->serialize());
}
const ConfigOption *opt = rhs.option(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.
this->set(opt_key, (opt->type() == coFloatOrPercent) ?
new ConfigOptionFloat(rhs.get_abs_value(opt_key)) :
opt->clone());
}
}
void PlaceholderParser::apply_env_variables()
{
for (char** env = environ; *env; env++) {
for (char** env = environ; *env; ++ env) {
if (strncmp(*env, "SLIC3R_", 7) == 0) {
std::stringstream ss(*env);
std::string key, value;
std::getline(ss, key, '=');
ss >> value;
this->set(key, value);
}
}
}
void PlaceholderParser::set(const std::string &key, const std::string &value)
{
m_single[key] = value;
m_multiple.erase(key);
}
namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
void PlaceholderParser::set(const std::string &key, int value)
namespace client
{
std::ostringstream ss;
ss << value;
this->set(key, ss.str());
}
template<typename Iterator>
struct OptWithPos {
OptWithPos() {}
OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range<Iterator> it_range) : opt(opt), it_range(it_range) {}
ConfigOptionConstPtr opt = nullptr;
boost::iterator_range<Iterator> it_range;
};
void PlaceholderParser::set(const std::string &key, unsigned int value)
{
std::ostringstream ss;
ss << value;
this->set(key, ss.str());
}
void PlaceholderParser::set(const std::string &key, double value)
{
std::ostringstream ss;
ss << value;
this->set(key, ss.str());
}
void PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
{
m_single.erase(key);
if (values.empty())
m_multiple.erase(key);
else
m_multiple[key] = values;
}
std::string PlaceholderParser::process(std::string str, unsigned int current_extruder_id) const
{
char key[2048];
// Replace extruder independent single options, like [foo].
for (const auto &key_value : m_single) {
sprintf(key, "[%s]", key_value.first.c_str());
const std::string &replace = key_value.second;
for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) {
str.replace(i, key_value.first.size() + 2, replace);
i += replace.size();
}
template<typename ITERATOR>
std::ostream& operator<<(std::ostream& os, OptWithPos<ITERATOR> const& opt)
{
os << std::string(opt.it_range.begin(), opt.it_range.end());
return os;
}
// Replace extruder dependent single options with the value for the active extruder.
// For example, [temperature] will be replaced with the current extruder temperature.
for (const auto &key_value : m_multiple) {
sprintf(key, "[%s]", key_value.first.c_str());
const std::string &replace = key_value.second[(current_extruder_id < key_value.second.size()) ? current_extruder_id : 0];
for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) {
str.replace(i, key_value.first.size() + 2, replace);
i += replace.size();
template<typename Iterator>
struct expr
{
expr() : type(TYPE_EMPTY) {}
explicit expr(bool b) : type(TYPE_BOOL) { data.b = b; }
explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_BOOL), it_range(it_begin, it_end) { data.b = b; }
explicit expr(int i) : type(TYPE_INT) { data.i = i; }
explicit expr(int i, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_INT), it_range(it_begin, it_end) { data.i = i; }
explicit expr(double d) : type(TYPE_DOUBLE) { data.d = d; }
explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_DOUBLE), it_range(it_begin, it_end) { data.d = d; }
explicit expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); }
explicit expr(const std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); }
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) :
type(TYPE_STRING), it_range(it_begin, it_end) { data.s = new std::string(s); }
expr(const expr &rhs) : type(rhs.type), it_range(rhs.it_range)
{ if (rhs.type == TYPE_STRING) data.s = new std::string(*rhs.data.s); else data.set(rhs.data); }
explicit expr(expr &&rhs) : type(rhs.type), it_range(rhs.it_range)
{ data.set(rhs.data); rhs.type = TYPE_EMPTY; }
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : type(rhs.type), it_range(it_begin, it_end)
{ data.set(rhs.data); rhs.type = TYPE_EMPTY; }
~expr() { this->reset(); }
expr &operator=(const expr &rhs)
{
this->type = rhs.type;
this->it_range = rhs.it_range;
if (rhs.type == TYPE_STRING)
this->data.s = new std::string(*rhs.data.s);
else
this->data.set(rhs.data);
return *this;
}
expr &operator=(expr &&rhs)
{
type = rhs.type;
this->it_range = rhs.it_range;
data.set(rhs.data);
rhs.type = TYPE_EMPTY;
return *this;
}
void reset()
{
if (this->type == TYPE_STRING)
delete data.s;
this->type = TYPE_EMPTY;
}
bool& b() { return data.b; }
bool b() const { return data.b; }
void set_b(bool v) { this->reset(); this->data.b = v; this->type = TYPE_BOOL; }
int& i() { return data.i; }
int i() const { return data.i; }
void set_i(int v) { this->reset(); this->data.i = v; this->type = TYPE_INT; }
int as_i() const { return (this->type == TYPE_INT) ? this->i() : int(this->d()); }
double& d() { return data.d; }
double d() const { return data.d; }
void set_d(double v) { this->reset(); this->data.d = v; this->type = TYPE_DOUBLE; }
double as_d() const { return (this->type == TYPE_DOUBLE) ? this->d() : double(this->i()); }
std::string& s() { return *data.s; }
const std::string& s() const { return *data.s; }
void set_s(const std::string &s) { this->reset(); this->data.s = new std::string(s); this->type = TYPE_STRING; }
void set_s(std::string &&s) { this->reset(); this->data.s = new std::string(std::move(s)); this->type = TYPE_STRING; }
std::string to_string() const
{
std::string out;
switch (type) {
case TYPE_BOOL: out = boost::to_string(data.b); break;
case TYPE_INT: out = boost::to_string(data.i); break;
case TYPE_DOUBLE: out = boost::to_string(data.d); break;
case TYPE_STRING: out = *data.s; break;
default: break;
}
return out;
}
union Data {
// Raw image of the other data members.
// The C++ compiler will consider a possible aliasing of char* with any other union member,
// therefore copying the raw data is safe.
char raw[8];
bool b;
int i;
double d;
std::string *s;
// Copy the largest member variable through char*, which will alias with all other union members by default.
void set(const Data &rhs) { memcpy(this->raw, rhs.raw, sizeof(rhs.raw)); }
} data;
enum Type {
TYPE_EMPTY = 0,
TYPE_BOOL,
TYPE_INT,
TYPE_DOUBLE,
TYPE_STRING,
};
Type type;
// Range of input iterators covering this expression.
// Used for throwing parse exceptions.
boost::iterator_range<Iterator> it_range;
expr unary_minus(const Iterator start_pos) const
{
switch (this->type) {
case TYPE_INT :
return expr<Iterator>(- this->i(), start_pos, this->it_range.end());
case TYPE_DOUBLE:
return expr<Iterator>(- this->d(), start_pos, this->it_range.end());
default:
this->throw_exception("Cannot apply unary minus operator.");
}
assert(false);
// Suppress compiler warnings.
return expr();
}
expr unary_not(const Iterator start_pos) const
{
switch (this->type) {
case TYPE_BOOL :
return expr<Iterator>(! this->b(), start_pos, this->it_range.end());
default:
this->throw_exception("Cannot apply a not operator.");
}
assert(false);
// Suppress compiler warnings.
return expr();
}
expr &operator+=(const expr &rhs)
{
const char *err_msg = "Cannot multiply with non-numeric type.";
this->throw_if_not_numeric(err_msg);
rhs.throw_if_not_numeric(err_msg);
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = this->as_d() + rhs.as_d();
this->data.d = d;
this->type = TYPE_DOUBLE;
} else
this->data.i += rhs.i();
this->it_range = boost::iterator_range<Iterator>(this->it_range.begin(), rhs.it_range.end());
return *this;
}
expr &operator-=(const expr &rhs)
{
const char *err_msg = "Cannot multiply with non-numeric type.";
this->throw_if_not_numeric(err_msg);
rhs.throw_if_not_numeric(err_msg);
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = this->as_d() - rhs.as_d();
this->data.d = d;
this->type = TYPE_DOUBLE;
} else
this->data.i -= rhs.i();
this->it_range = boost::iterator_range<Iterator>(this->it_range.begin(), rhs.it_range.end());
return *this;
}
expr &operator*=(const expr &rhs)
{
const char *err_msg = "Cannot multiply with non-numeric type.";
this->throw_if_not_numeric(err_msg);
rhs.throw_if_not_numeric(err_msg);
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = this->as_d() * rhs.as_d();
this->data.d = d;
this->type = TYPE_DOUBLE;
} else
this->data.i *= rhs.i();
this->it_range = boost::iterator_range<Iterator>(this->it_range.begin(), rhs.it_range.end());
return *this;
}
expr &operator/=(const expr &rhs)
{
this->throw_if_not_numeric("Cannot divide a non-numeric type.");
rhs.throw_if_not_numeric("Cannot divide with a non-numeric type.");
if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.))
rhs.throw_exception("Division by zero");
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = this->as_d() / rhs.as_d();
this->data.d = d;
this->type = TYPE_DOUBLE;
} else
this->data.i /= rhs.i();
this->it_range = boost::iterator_range<Iterator>(this->it_range.begin(), rhs.it_range.end());
return *this;
}
static void to_string2(expr &self, std::string &out)
{
out = self.to_string();
}
static void evaluate_boolean(expr &self, bool &out)
{
if (self.type != TYPE_BOOL)
self.throw_exception("Not a boolean expression");
out = self.b();
}
// Is lhs==rhs? Store the result into lhs.
static void compare_op(expr &lhs, expr &rhs, char op)
{
bool value = false;
if ((lhs.type == TYPE_INT || lhs.type == TYPE_DOUBLE) &&
(rhs.type == TYPE_INT || rhs.type == TYPE_DOUBLE)) {
// Both types are numeric.
value = (lhs.type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) ?
(lhs.as_d() == rhs.as_d()) : (lhs.i() == rhs.i());
} else if (lhs.type == TYPE_BOOL && rhs.type == TYPE_BOOL) {
// Both type are bool.
value = lhs.b() == rhs.b();
} else if (lhs.type == TYPE_STRING || rhs.type == TYPE_STRING) {
// One type is string, the other could be converted to string.
value = lhs.to_string() == rhs.to_string();
} else {
boost::throw_exception(qi::expectation_failure<Iterator>(
lhs.it_range.begin(), rhs.it_range.end(), spirit::info("Cannot compare the types.")));
}
lhs.type = TYPE_BOOL;
lhs.data.b = (op == '=') ? value : !value;
}
static void equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '='); }
static void not_equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '!'); }
static void set_if(bool &cond, bool &not_yet_consumed, std::string &str_in, std::string &str_out)
{
if (cond && not_yet_consumed) {
str_out = str_in;
not_yet_consumed = false;
}
}
void throw_exception(const char *message) const
{
boost::throw_exception(qi::expectation_failure<Iterator>(
this->it_range.begin(), this->it_range.end(), spirit::info(message)));
}
void throw_if_not_numeric(const char *message) const
{
if (this->type != TYPE_INT && this->type != TYPE_DOUBLE)
this->throw_exception(message);
}
};
template<typename ITERATOR>
std::ostream& operator<<(std::ostream &os, const expr<ITERATOR> &expression)
{
typedef expr<ITERATOR> Expr;
os << std::string(expression.it_range.begin(), expression.it_range.end()) << " - ";
switch (expression.type) {
case Expr::TYPE_EMPTY: os << "empty"; break;
case Expr::TYPE_BOOL: os << "bool (" << expression.b() << ")"; break;
case Expr::TYPE_INT: os << "int (" << expression.i() << ")"; break;
case Expr::TYPE_DOUBLE: os << "double (" << expression.d() << ")"; break;
case Expr::TYPE_STRING: os << "string (" << expression.s() << ")"; break;
default: os << "unknown";
};
return os;
}
// Replace multiple options like [foo_0].
for (const auto &key_value : m_multiple) {
sprintf(key, "[%s_", key_value.first.c_str());
const std::vector<std::string> &values = key_value.second;
for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) {
size_t k = str.find(']', i + key_value.first.size() + 2);
if (k != std::string::npos) {
// Parse the key index and the closing bracket.
++ k;
int idx = 0;
if (sscanf(str.c_str() + i + key_value.first.size() + 2, "%d]", &idx) == 1 && idx >= 0) {
if (idx >= int(values.size()))
idx = 0;
str.replace(i, k - i, values[idx]);
i += values[idx].size();
continue;
struct MyContext {
const PlaceholderParser *pp = nullptr;
const DynamicConfig *config_override = nullptr;
const size_t current_extruder_id = 0;
const ConfigOption* resolve_symbol(const std::string &opt_key) const
{
const ConfigOption *opt = nullptr;
if (config_override != nullptr)
opt = config_override->option(opt_key);
if (opt == nullptr)
opt = pp->option(opt_key);
return opt;
}
template <typename Iterator>
static void legacy_variable_expansion(
const MyContext *ctx,
boost::iterator_range<Iterator> &opt_key,
std::string &output)
{
std::string opt_key_str(opt_key.begin(), opt_key.end());
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
size_t idx = ctx->current_extruder_id;
if (opt == nullptr) {
// Check whether this is a legacy vector indexing.
idx = opt_key_str.rfind('_');
if (idx != std::string::npos) {
opt = ctx->resolve_symbol(opt_key_str.substr(0, idx));
if (opt != nullptr) {
if (! opt->is_vector())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable")));
char *endptr = nullptr;
idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10);
if (endptr == nullptr || *endptr != 0)
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin() + idx + 1, opt_key.end(), spirit::info("Invalid vector index")));
}
}
}
// The key does not match the pattern [foo_%d]. Skip just [foo_.] with the hope that there was a missing ']',
// so an opening '[' may be found somewhere before the position k.
i += key_value.first.size() + 3;
if (opt == nullptr)
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist")));
if (opt->is_scalar())
output = opt->serialize();
else {
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
if (vec->empty())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable")));
output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx];
}
}
template <typename Iterator>
static void legacy_variable_expansion2(
const MyContext *ctx,
boost::iterator_range<Iterator> &opt_key,
boost::iterator_range<Iterator> &opt_vector_index,
std::string &output)
{
std::string opt_key_str(opt_key.begin(), opt_key.end());
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
if (opt == nullptr) {
// Check whether the opt_key ends with '_'.
if (opt_key_str.back() == '_')
opt_key_str.resize(opt_key_str.size() - 1);
opt = ctx->resolve_symbol(opt_key_str);
}
if (! opt->is_vector())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable")));
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
if (vec->empty())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable")));
const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end()));
if (opt_index == nullptr)
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist")));
if (opt_index->type() != coInt)
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Indexing variable has to be integer")));
int idx = opt_index->getInt();
if (idx < 0)
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Negative vector index")));
output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx];
}
template <typename Iterator>
static void resolve_variable(
const MyContext *ctx,
boost::iterator_range<Iterator> &opt_key,
OptWithPos<Iterator> &output)
{
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end()));
if (opt == nullptr)
boost::throw_exception(qi::expectation_failure<Iterator>(
opt_key.begin(), opt_key.end(), spirit::info("Not a variable name")));
output.opt = opt;
output.it_range = opt_key;
}
template <typename Iterator>
static void scalar_variable_reference(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
expr<Iterator> &output)
{
if (opt.opt->is_vector())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a scalar variable in a vector context")));
switch (opt.opt->type()) {
case coFloat: output.set_d(opt.opt->getFloat()); break;
case coInt: output.set_i(opt.opt->getInt()); break;
case coString: output.set_s(static_cast<const ConfigOptionString*>(opt.opt)->value); break;
case coPercent: output.set_d(opt.opt->getFloat()); break;
case coPoint: output.set_s(opt.opt->serialize()); break;
case coBool: output.set_b(opt.opt->getBool()); break;
case coFloatOrPercent:
boost::throw_exception(qi::expectation_failure<Iterator>(
opt.it_range.begin(), opt.it_range.end(), spirit::info("FloatOrPercent variables are not supported")));
default:
boost::throw_exception(qi::expectation_failure<Iterator>(
opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown scalar variable type")));
}
output.it_range = opt.it_range;
}
template <typename Iterator>
static void vector_variable_reference(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
int &index,
Iterator it_end,
expr<Iterator> &output)
{
if (opt.opt->is_scalar())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a vector variable in a scalar context")));
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
boost::throw_exception(qi::expectation_failure<Iterator>(
opt.it_range.begin(), opt.it_range.end(), spirit::info("Indexing an empty vector variable")));
size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index);
switch (opt.opt->type()) {
case coFloats: output.set_d(static_cast<const ConfigOptionFloats *>(opt.opt)->values[idx]); break;
case coInts: output.set_i(static_cast<const ConfigOptionInts *>(opt.opt)->values[idx]); break;
case coStrings: output.set_s(static_cast<const ConfigOptionStrings *>(opt.opt)->values[idx]); break;
case coPercents: output.set_d(static_cast<const ConfigOptionPercents*>(opt.opt)->values[idx]); break;
case coPoints: output.set_s(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx].dump_perl()); break;
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
default:
boost::throw_exception(qi::expectation_failure<Iterator>(
opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown vector variable type")));
}
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
}
// Verify that the expression returns an integer, which may be used
// to address a vector.
template <typename Iterator>
static void evaluate_index(expr<Iterator> &expr_index, int &output)
{
if (expr_index.type != expr<Iterator>::TYPE_INT)
expr_index.throw_exception("Non-integer index is not allowed to address a vector variable.");
output = expr_index.i();
}
};
// For debugging the boost::spirit parsers. Print out the string enclosed in it_range.
template<typename Iterator>
std::ostream& operator<<(std::ostream& os, const boost::iterator_range<Iterator> &it_range)
{
os << std::string(it_range.begin(), it_range.end());
return os;
}
return str;
// Disable parsing int numbers (without decimals) and Inf/NaN symbols by the double parser.
struct strict_real_policies_without_nan_inf : public qi::strict_real_policies<double>
{
template <typename It, typename Attr> static bool parse_nan(It&, It const&, Attr&) { return false; }
template <typename It, typename Attr> static bool parse_inf(It&, It const&, Attr&) { return false; }
};
///////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
template <typename Iterator>
struct calculator : qi::grammar<Iterator, std::string(const MyContext*), spirit::ascii::space_type>
{
calculator() : calculator::base_type(start)
{
using namespace qi::labels;
qi::alpha_type alpha;
qi::alnum_type alnum;
qi::eps_type eps;
qi::raw_type raw;
qi::lit_type lit;
qi::lexeme_type lexeme;
qi::no_skip_type no_skip;
qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double;
spirit::ascii::char_type char_;
spirit::bool_type bool_;
spirit::int_type int_;
spirit::double_type double_;
spirit::ascii::string_type string;
spirit::repository::qi::iter_pos_type iter_pos;
auto kw = spirit::repository::qi::distinct(qi::copy(alnum | '_'));
qi::_val_type _val;
qi::_1_type _1;
qi::_2_type _2;
qi::_a_type _a;
qi::_b_type _b;
qi::_r1_type _r1;
// Starting symbol of the grammer.
// The leading eps is required by the "expectation point" operator ">".
// Without it, some of the errors would not trigger the error handler.
start = eps > text_block(_r1);
start.name("start");
text_block = *(
text [_val+=_1]
// Allow back tracking after '{' in case of a text_block embedded inside a condition.
// In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired.
// {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block.
| (lit('{') >> macro(_r1) [_val+=_1] > '}')
| (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')
);
text_block.name("text_block");
// Free-form text up to a first brace, including spaces and newlines.
// The free-form text will be inserted into the processed text without a modification.
text = no_skip[raw[+(char_ - '[' - '{')]];
text.name("text");
// New style of macro expansion.
// The macro expansion may contain numeric or string expressions, ifs and cases.
macro =
(kw["if"] > if_else_output(_r1) [_val = _1])
| (kw["switch"] > switch_output(_r1) [_val = _1])
| additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ];
macro.name("macro");
// An if expression enclosed in {} (the outmost {} are already parsed by the caller).
if_else_output =
eps[_b=true] >
bool_expr_eval(_r1)[_a=_1] > '}' >
text_block(_r1)[px::bind(&expr<Iterator>::set_if, _a, _b, _1, _val)] > '{' >
*(kw["elsif"] > bool_expr_eval(_r1)[_a=_1] > '}' >
text_block(_r1)[px::bind(&expr<Iterator>::set_if, _a, _b, _1, _val)] > '{') >
-(kw["else"] > lit('}') >
text_block(_r1)[px::bind(&expr<Iterator>::set_if, _b, _b, _1, _val)] > '{') >
kw["endif"];
if_else_output.name("if_else_output");
// A switch expression enclosed in {} (the outmost {} are already parsed by the caller).
/*
switch_output =
eps[_b=true] >
omit[expr(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr<Iterator>::set_if_equal, _a, _b, _1, _val)] > '{' >
*("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr<Iterator>::set_if, _a, _b, _1, _val)]) >>
-("else" > '}' >> text_block(_r1)[px::bind(&expr<Iterator>::set_if, _b, _b, _1, _val)]) >
"endif";
*/
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
legacy_variable_expansion =
(identifier >> &lit(']'))
[ px::bind(&MyContext::legacy_variable_expansion<Iterator>, _r1, _1, _val) ]
| (identifier > lit('[') > identifier > ']')
[ px::bind(&MyContext::legacy_variable_expansion2<Iterator>, _r1, _1, _2, _val) ]
;
legacy_variable_expansion.name("legacy_variable_expansion");
identifier =
! kw[keywords] >>
raw[lexeme[(alpha | '_') >> *(alnum | '_')]];
identifier.name("identifier");
bool_expr =
additive_expression(_r1) [_val = _1]
>> *( ("==" > additive_expression(_r1) ) [px::bind(&expr<Iterator>::equal, _val, _1)]
| ("!=" > additive_expression(_r1) ) [px::bind(&expr<Iterator>::not_equal, _val, _1)]
| ("<>" > additive_expression(_r1) ) [px::bind(&expr<Iterator>::not_equal, _val, _1)]
);
bool_expr.name("bool expression");
// Evaluate a boolean expression stored as expr into a boolean value.
// Throw if the bool_expr does not produce a expr of boolean type.
bool_expr_eval = bool_expr(_r1) [ px::bind(&expr<Iterator>::evaluate_boolean, _1, _val) ];
bool_expr_eval.name("bool_expr_eval");
additive_expression =
term(_r1) [_val = _1]
>> *( (lit('+') > term(_r1) ) [_val += _1]
| (lit('-') > term(_r1) ) [_val -= _1]
);
additive_expression.name("additive_expression");
term =
factor(_r1) [_val = _1]
>> *( (lit('*') > factor(_r1) ) [_val *= _1]
| (lit('/') > factor(_r1) ) [_val /= _1]
);
term.name("term");
struct FactorActions {
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)
{ out.it_range = boost::iterator_range<Iterator>(start_pos, start_pos); }
static void int_(int &value, Iterator &end_pos, expr<Iterator> &out)
{ out = expr<Iterator>(value, out.it_range.begin(), end_pos); }
static void double_(double &value, Iterator &end_pos, expr<Iterator> &out)
{ out = expr<Iterator>(value, out.it_range.begin(), end_pos); }
static void bool_(bool &value, Iterator &end_pos, expr<Iterator> &out)
{ out = expr<Iterator>(value, out.it_range.begin(), end_pos); }
static void string_(boost::iterator_range<Iterator> &it_range, expr<Iterator> &out)
{ out = expr<Iterator>(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); }
static void expr_(expr<Iterator> &value, Iterator &end_pos, expr<Iterator> &out)
{ out = expr<Iterator>(std::move(value), out.it_range.begin(), end_pos); }
static void minus_(expr<Iterator> &value, expr<Iterator> &out)
{ out = value.unary_minus(out.it_range.begin()); }
static void not_(expr<Iterator> &value, expr<Iterator> &out)
{ out = value.unary_not(out.it_range.begin()); }
};
factor = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
scalar_variable_reference(_r1) [ _val = _1 ]
| (lit('(') > additive_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ]
| (lit('-') > factor(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ]
| (lit('+') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ]
| ((kw["not"] | '!') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
| raw[lexeme['"' > *((char_ - char_('\\') - char_('"')) | ('\\' > char_)) > '"']]
[ px::bind(&FactorActions::string_, _1, _val) ]
);
factor.name("factor");
scalar_variable_reference =
variable_reference(_r1)[_a=_1] >>
(
('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index<Iterator>, _1, _b)] > ']' >
iter_pos[px::bind(&MyContext::vector_variable_reference<Iterator>, _r1, _a, _b, _1, _val)])
| eps[px::bind(&MyContext::scalar_variable_reference<Iterator>, _r1, _a, _val)]
);
scalar_variable_reference.name("scalar variable reference");
variable_reference = identifier
[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
variable_reference.name("variable reference");
/*
qi::on_error<qi::fail>(start,
phx::ref(std::cout)
<< "Error! Expecting "
<< qi::_4
<< " here: '"
<< px::construct<std::string>(qi::_3, qi::_2)
<< "'\n"
);
*/
keywords.add
("and")
("if")
//("inf")
("else")
("elsif")
("endif")
("false")
("not")
("or")
("true");
if (0) {
debug(start);
debug(text);
debug(text_block);
debug(macro);
debug(if_else_output);
debug(switch_output);
debug(legacy_variable_expansion);
debug(identifier);
debug(bool_expr);
debug(bool_expr_eval);
debug(additive_expression);
debug(term);
debug(factor);
debug(scalar_variable_reference);
debug(variable_reference);
}
}
// The start of the grammar.
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> start;
// A free-form text.
qi::rule<Iterator, std::string(), spirit::ascii::space_type> text;
// A free-form text, possibly empty, possibly containing macro expansions.
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> text_block;
// Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> macro;
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> legacy_variable_expansion;
// Parsed identifier name.
qi::rule<Iterator, boost::iterator_range<Iterator>(), spirit::ascii::space_type> identifier;
// Math expression consisting of +- operators over terms.
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit::ascii::space_type> additive_expression;
// Boolean expressions over expressions.
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit::ascii::space_type> bool_expr;
// Evaluate boolean expression into bool.
qi::rule<Iterator, bool(const MyContext*), spirit::ascii::space_type> bool_expr_eval;
// Math expression consisting of */ operators over factors.
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit::ascii::space_type> term;
// Number literals, functions, braced expressions, variable references, variable indexing references.
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit::ascii::space_type> factor;
// Reference of a scalar variable, or reference to a field of a vector variable.
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit::ascii::space_type> scalar_variable_reference;
// Rule to translate an identifier to a ConfigOption, or to fail.
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit::ascii::space_type> variable_reference;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit::ascii::space_type> if_else_output;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit::ascii::space_type> switch_output;
qi::symbols<char> keywords;
};
}
struct printer
{
typedef spirit::utf8_string string;
void element(string const& tag, string const& value, int depth) const
{
for (int i = 0; i < (depth*4); ++i) // indent to depth
std::cout << ' ';
std::cout << "tag: " << tag;
if (value != "")
std::cout << ", value: " << value;
std::cout << std::endl;
}
};
void print_info(spirit::info const& what)
{
using spirit::basic_info_walker;
printer pr;
basic_info_walker<printer> walker(pr, what.tag, 0);
boost::apply_visitor(walker, what.value);
}
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
{
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
spirit::ascii::space_type space; // Our skipper
calculator calc; // Our grammar
std::string::const_iterator iter = templ.begin();
std::string::const_iterator end = templ.end();
//std::string result;
std::string result;
bool r = false;
try {
client::MyContext context;
context.pp = this;
context.config_override = config_override;
r = phrase_parse(iter, end, calc(&context), space, result);
} catch (qi::expectation_failure<iterator_type> const& x) {
std::cout << "expected: "; print_info(x.what_);
std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl;
}
if (r && iter == end)
{
// std::cout << "-------------------------\n";
// std::cout << "Parsing succeeded\n";
// std::cout << "result = " << result << std::endl;
// std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \" " << rest << "\"\n";
std::cout << "source: \n" << templ;
std::cout << "-------------------------\n";
}
return result;
}
}

View File

@@ -11,23 +11,27 @@ namespace Slic3r {
class PlaceholderParser
{
public:
std::map<std::string, std::string> m_single;
std::map<std::string, std::vector<std::string>> m_multiple;
public:
PlaceholderParser();
void update_timestamp();
void apply_config(const DynamicPrintConfig &config);
void apply_env_variables();
void set(const std::string &key, const std::string &value);
void set(const std::string &key, int value);
void set(const std::string &key, unsigned int value);
void set(const std::string &key, double value);
void set(const std::string &key, std::vector<std::string> values);
std::string process(std::string str, unsigned int current_extruder_id) const;
// Add new ConfigOption values to m_config.
void set(const std::string &key, const std::string &value) { this->set(key, new ConfigOptionString(value)); }
void set(const std::string &key, int value) { this->set(key, new ConfigOptionInt(value)); }
void set(const std::string &key, unsigned int value) { this->set(key, int(value)); }
void set(const std::string &key, double value) { this->set(key, new ConfigOptionFloat(value)); }
void set(const std::string &key, const std::vector<std::string> &values) { this->set(key, new ConfigOptionStrings(values)); }
void set(const std::string &key, ConfigOption *opt) { m_config.set_key_value(key, opt); }
const ConfigOption* option(const std::string &key) const { return m_config.option(key); }
// Fill in the template.
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr) const;
private:
bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const;
DynamicConfig m_config;
};
}

View File

@@ -154,7 +154,6 @@ inline Polylines to_polylines(const Polygons &polys)
return polylines;
}
#if SLIC3R_CPPVER >= 11
inline Polylines to_polylines(Polygons &&polys)
{
Polylines polylines;
@@ -168,7 +167,6 @@ inline Polylines to_polylines(Polygons &&polys)
assert(idx == polylines.size());
return polylines;
}
#endif
} // Slic3r

View File

@@ -81,80 +81,80 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
// Cache the plenty of parameters, which influence the G-code generator only,
// or they are only notes not influencing the generated G-code.
static std::unordered_set<std::string> steps_ignore;
if (steps_ignore.empty()) {
steps_ignore.insert("avoid_crossing_perimeters");
steps_ignore.insert("bed_shape");
steps_ignore.insert("bed_temperature");
steps_ignore.insert("before_layer_gcode");
steps_ignore.insert("bridge_acceleration");
steps_ignore.insert("bridge_fan_speed");
steps_ignore.insert("cooling");
steps_ignore.insert("default_acceleration");
steps_ignore.insert("deretract_speed");
steps_ignore.insert("disable_fan_first_layers");
steps_ignore.insert("duplicate_distance");
steps_ignore.insert("end_gcode");
steps_ignore.insert("end_filament_gcode");
steps_ignore.insert("extrusion_axis");
steps_ignore.insert("extruder_clearance_height");
steps_ignore.insert("extruder_clearance_radius");
steps_ignore.insert("extruder_colour");
steps_ignore.insert("extruder_offset");
steps_ignore.insert("extrusion_multiplier");
steps_ignore.insert("fan_always_on");
steps_ignore.insert("fan_below_layer_time");
steps_ignore.insert("filament_colour");
steps_ignore.insert("filament_diameter");
steps_ignore.insert("filament_density");
steps_ignore.insert("filament_notes");
steps_ignore.insert("filament_cost");
steps_ignore.insert("filament_max_volumetric_speed");
steps_ignore.insert("first_layer_acceleration");
steps_ignore.insert("first_layer_bed_temperature");
steps_ignore.insert("first_layer_speed");
steps_ignore.insert("gcode_comments");
steps_ignore.insert("gcode_flavor");
steps_ignore.insert("infill_acceleration");
steps_ignore.insert("infill_first");
steps_ignore.insert("layer_gcode");
steps_ignore.insert("min_fan_speed");
steps_ignore.insert("max_fan_speed");
steps_ignore.insert("min_print_speed");
steps_ignore.insert("max_print_speed");
steps_ignore.insert("max_volumetric_speed");
steps_ignore.insert("max_volumetric_extrusion_rate_slope_positive");
steps_ignore.insert("max_volumetric_extrusion_rate_slope_negative");
steps_ignore.insert("notes");
steps_ignore.insert("only_retract_when_crossing_perimeters");
steps_ignore.insert("output_filename_format");
steps_ignore.insert("perimeter_acceleration");
steps_ignore.insert("post_process");
steps_ignore.insert("printer_notes");
steps_ignore.insert("retract_before_travel");
steps_ignore.insert("retract_before_wipe");
steps_ignore.insert("retract_layer_change");
steps_ignore.insert("retract_length");
steps_ignore.insert("retract_length_toolchange");
steps_ignore.insert("retract_lift");
steps_ignore.insert("retract_lift_above");
steps_ignore.insert("retract_lift_below");
steps_ignore.insert("retract_restart_extra");
steps_ignore.insert("retract_restart_extra_toolchange");
steps_ignore.insert("retract_speed");
steps_ignore.insert("slowdown_below_layer_time");
steps_ignore.insert("standby_temperature_delta");
steps_ignore.insert("start_gcode");
steps_ignore.insert("start_filament_gcode");
steps_ignore.insert("toolchange_gcode");
steps_ignore.insert("threads");
steps_ignore.insert("travel_speed");
steps_ignore.insert("use_firmware_retraction");
steps_ignore.insert("use_relative_e_distances");
steps_ignore.insert("use_volumetric_e");
steps_ignore.insert("variable_layer_height");
steps_ignore.insert("wipe");
}
static std::unordered_set<std::string> steps_ignore = {
"avoid_crossing_perimeters",
"bed_shape",
"bed_temperature",
"before_layer_gcode",
"between_objects_gcode",
"bridge_acceleration",
"bridge_fan_speed",
"cooling",
"default_acceleration",
"deretract_speed",
"disable_fan_first_layers",
"duplicate_distance",
"end_gcode",
"end_filament_gcode",
"extrusion_axis",
"extruder_clearance_height",
"extruder_clearance_radius",
"extruder_colour",
"extruder_offset",
"extrusion_multiplier",
"fan_always_on",
"fan_below_layer_time",
"filament_colour",
"filament_diameter",
"filament_density",
"filament_notes",
"filament_cost",
"filament_max_volumetric_speed",
"first_layer_acceleration",
"first_layer_bed_temperature",
"first_layer_speed",
"gcode_comments",
"gcode_flavor",
"infill_acceleration",
"infill_first",
"layer_gcode",
"min_fan_speed",
"max_fan_speed",
"min_print_speed",
"max_print_speed",
"max_volumetric_speed",
"max_volumetric_extrusion_rate_slope_positive",
"max_volumetric_extrusion_rate_slope_negative",
"notes",
"only_retract_when_crossing_perimeters",
"output_filename_format",
"perimeter_acceleration",
"post_process",
"printer_notes",
"retract_before_travel",
"retract_before_wipe",
"retract_layer_change",
"retract_length",
"retract_length_toolchange",
"retract_lift",
"retract_lift_above",
"retract_lift_below",
"retract_restart_extra",
"retract_restart_extra_toolchange",
"retract_speed",
"slowdown_below_layer_time",
"standby_temperature_delta",
"start_gcode",
"start_filament_gcode",
"toolchange_gcode",
"threads",
"travel_speed",
"use_firmware_retraction",
"use_relative_e_distances",
"use_volumetric_e",
"variable_layer_height",
"wipe"
};
std::vector<PrintStep> steps;
std::vector<PrintObjectStep> osteps;
@@ -707,7 +707,10 @@ double Print::skirt_first_layer_height() const
Flow Print::brim_flow() const
{
ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width;
if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width;
if (width.value == 0)
width = this->regions.front()->config.perimeter_extrusion_width;
if (width.value == 0)
width = this->objects.front()->config.extrusion_width;
/* We currently use a random region's perimeter extruder.
While this works for most cases, we should probably consider all of the perimeter
@@ -726,7 +729,10 @@ Flow Print::brim_flow() const
Flow Print::skirt_flow() const
{
ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width;
if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width;
if (width.value == 0)
width = this->regions.front()->config.perimeter_extrusion_width;
if (width.value == 0)
width = this->objects.front()->config.extrusion_width;
/* We currently use a random object's support material extruder.
While this works for most cases, we should probably consider all of the support material
@@ -1079,13 +1085,13 @@ std::string Print::output_filepath(const std::string &path)
if (! input_file.empty())
break;
}
return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).string();
return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string();
}
// if we were supplied a directory, use it and append our automatically generated filename
boost::filesystem::path p(path);
if (boost::filesystem::is_directory(p))
return (p / this->output_filename()).string();
return (p / this->output_filename()).make_preferred().string();
// if we were supplied a file which is not a directory, use it
return path;

File diff suppressed because it is too large Load Diff

View File

@@ -142,6 +142,9 @@ public:
DynamicPrintConfig() {}
DynamicPrintConfig(const DynamicPrintConfig &other) : DynamicConfig(other) {}
static DynamicPrintConfig* new_from_defaults();
static DynamicPrintConfig* new_from_defaults_keys(const std::vector<std::string> &keys);
// 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; }
@@ -449,6 +452,7 @@ class GCodeConfig : public StaticPrintConfig
STATIC_PRINT_CONFIG_CACHE(GCodeConfig)
public:
ConfigOptionString before_layer_gcode;
ConfigOptionString between_objects_gcode;
ConfigOptionFloats deretract_speed;
ConfigOptionString end_gcode;
ConfigOptionStrings end_filament_gcode;
@@ -497,6 +501,7 @@ protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
OPT_PTR(before_layer_gcode);
OPT_PTR(between_objects_gcode);
OPT_PTR(deretract_speed);
OPT_PTR(end_gcode);
OPT_PTR(end_filament_gcode);

View File

@@ -1208,6 +1208,8 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
// perform a safety offset to merge very close facets (TODO: find test case for this)
double safety_offset = scale_(0.0499);
//FIXME see https://github.com/prusa3d/Slic3r/issues/520
// double safety_offset = scale_(0.0001);
ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
#ifdef SLIC3R_TRIANGLEMESH_DEBUG

View File

@@ -1,6 +1,8 @@
#ifndef slic3r_Utils_hpp_
#define slic3r_Utils_hpp_
#include <locale>
namespace Slic3r {
extern void set_logging_level(unsigned int level);
@@ -8,15 +10,31 @@ extern void trace(unsigned int level, const char *message);
// Set a path with GUI resource files.
void set_var_dir(const std::string &path);
// Return a path to the GUI resource files.
// Return a full path to the GUI resource files.
const std::string& var_dir();
// Return a resource path for a file_name.
// Return a full resource path for a file_name.
std::string var(const std::string &file_name);
// Set a path with preset files.
void set_data_dir(const std::string &path);
// Return a full path to the GUI resource files.
const std::string& data_dir();
// Return a full path to a configuration file given its file name..
std::string config_path(const std::string &file_name);
// Return a full path to a configuration file given the section and name.
// The suffix ".ini" will be added if it is missing in the name.
std::string config_path(const std::string &section, const std::string &name);
extern std::string encode_path(const char *src);
extern std::string decode_path(const char *src);
extern std::string normalize_utf8_nfc(const char *src);
// Timestamp formatted for header_slic3r_generated().
extern std::string timestamp_str();
// Standard "generated by Slic3r version xxx timestamp xxx" header string,
// to be placed at the top of Slic3r generated files.
inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
// Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html
template<typename T>

View File

@@ -14,7 +14,7 @@
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.33.8.devel"
#define SLIC3R_VERSION "1.38.2"
#define SLIC3R_BUILD "UNKNOWN"
typedef long coord_t;

View File

@@ -1,4 +1,5 @@
#include <locale>
#include <ctime>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
@@ -6,8 +7,9 @@
#include <boost/locale.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/filesystem.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
@@ -87,6 +89,31 @@ std::string var(const std::string &file_name)
return file.string();
}
static std::string g_data_dir;
void set_data_dir(const std::string &dir)
{
g_data_dir = dir;
}
const std::string& data_dir()
{
return g_data_dir;
}
std::string config_path(const std::string &file_name)
{
auto file = (boost::filesystem::path(g_data_dir) / file_name).make_preferred();
return file.string();
}
std::string config_path(const std::string &section, const std::string &name)
{
auto file_name = boost::algorithm::iends_with(name, ".ini") ? name : name + ".ini";
auto file = (boost::filesystem::path(g_data_dir) / section / file_name).make_preferred();
return file.string();
}
} // namespace Slic3r
#ifdef SLIC3R_HAS_BROKEN_CROAK
@@ -207,4 +234,16 @@ std::string normalize_utf8_nfc(const char *src)
return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8);
}
std::string timestamp_str()
{
const auto now = boost::posix_time::second_clock::local_time();
const auto date = now.date();
char buf[2048];
sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d",
// Local date in an ANSII format.
int(now.date().year()), int(now.date().month()), int(now.date().day()),
int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds()));
return buf;
}
}; // namespace Slic3r