PlaceholderParser: new interpolate_table() "function"
interpolate_table(x, (x0, y0), (x1, y1), (x2, y2), ...) interpolates a table at position x.
This commit is contained in:
parent
c5b43107ba
commit
963ca415d4
@ -249,6 +249,7 @@ namespace client
|
|||||||
TYPE_STRING,
|
TYPE_STRING,
|
||||||
};
|
};
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
|
bool numeric_type() const { return m_type == TYPE_INT || m_type == TYPE_DOUBLE; }
|
||||||
|
|
||||||
bool& b() { return m_data.b; }
|
bool& b() { return m_data.b; }
|
||||||
bool b() const { return m_data.b; }
|
bool b() const { return m_data.b; }
|
||||||
@ -472,8 +473,7 @@ namespace client
|
|||||||
static void compare_op(expr &lhs, expr &rhs, char op, bool invert)
|
static void compare_op(expr &lhs, expr &rhs, char op, bool invert)
|
||||||
{
|
{
|
||||||
bool value = false;
|
bool value = false;
|
||||||
if ((lhs.type() == TYPE_INT || lhs.type() == TYPE_DOUBLE) &&
|
if (lhs.numeric_type() && rhs.numeric_type()) {
|
||||||
(rhs.type() == TYPE_INT || rhs.type() == TYPE_DOUBLE)) {
|
|
||||||
// Both types are numeric.
|
// Both types are numeric.
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case '=':
|
case '=':
|
||||||
@ -681,7 +681,7 @@ namespace client
|
|||||||
|
|
||||||
void throw_if_not_numeric(const char *message) const
|
void throw_if_not_numeric(const char *message) const
|
||||||
{
|
{
|
||||||
if (this->type() != TYPE_INT && this->type() != TYPE_DOUBLE)
|
if (! this->numeric_type())
|
||||||
this->throw_exception(message);
|
this->throw_exception(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -964,6 +964,10 @@ namespace client
|
|||||||
{
|
{
|
||||||
if (! opt.writable)
|
if (! opt.writable)
|
||||||
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
||||||
|
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||||
|
if (! param.numeric_type())
|
||||||
|
param.throw_exception("Right side is not a numeric expression");
|
||||||
|
};
|
||||||
if (opt.opt->is_vector()) {
|
if (opt.opt->is_vector()) {
|
||||||
if (! opt.has_index())
|
if (! opt.has_index())
|
||||||
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
|
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
|
||||||
@ -974,21 +978,18 @@ namespace client
|
|||||||
ctx->throw_exception("Index out of range", opt.it_range);
|
ctx->throw_exception("Index out of range", opt.it_range);
|
||||||
switch (opt.opt->type()) {
|
switch (opt.opt->type()) {
|
||||||
case coFloats:
|
case coFloats:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
|
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coInts:
|
case coInts:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
|
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
|
||||||
break;
|
break;
|
||||||
case coStrings:
|
case coStrings:
|
||||||
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
|
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
|
||||||
break;
|
break;
|
||||||
case coPercents:
|
case coPercents:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
|
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coBools:
|
case coBools:
|
||||||
@ -1004,21 +1005,18 @@ namespace client
|
|||||||
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
|
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
|
||||||
switch (wropt->type()) {
|
switch (wropt->type()) {
|
||||||
case coFloat:
|
case coFloat:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
|
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coInt:
|
case coInt:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
|
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
|
||||||
break;
|
break;
|
||||||
case coString:
|
case coString:
|
||||||
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
|
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
|
||||||
break;
|
break;
|
||||||
case coPercent:
|
case coPercent:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
|
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coBool:
|
case coBool:
|
||||||
@ -1109,6 +1107,72 @@ namespace client
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
struct InterpolateTableContext {
|
||||||
|
template<typename Iterator>
|
||||||
|
struct Item {
|
||||||
|
double x;
|
||||||
|
boost::iterator_range<Iterator> it_range_x;
|
||||||
|
double y;
|
||||||
|
};
|
||||||
|
std::vector<Item<Iterator>> table;
|
||||||
|
|
||||||
|
static void init(const expr<Iterator> &x) {
|
||||||
|
if (!x.numeric_type())
|
||||||
|
x.throw_exception("Interpolation value must be a number.");
|
||||||
|
}
|
||||||
|
static void add_pair(const expr<Iterator> &x, const expr<Iterator> &y, InterpolateTableContext &table) {
|
||||||
|
if (! x.numeric_type())
|
||||||
|
x.throw_exception("X value of a table point must be a number.");
|
||||||
|
if (! y.numeric_type())
|
||||||
|
y.throw_exception("Y value of a table point must be a number.");
|
||||||
|
table.table.push_back({ x.as_d(), x.it_range, y.as_d() });
|
||||||
|
}
|
||||||
|
static void evaluate(const expr<Iterator> &expr_x, const InterpolateTableContext &table, expr<Iterator> &out) {
|
||||||
|
// Check whether the table X values are sorted.
|
||||||
|
double x = expr_x.as_d();
|
||||||
|
bool evaluated = false;
|
||||||
|
for (size_t i = 1; i < table.table.size(); ++i) {
|
||||||
|
double x0 = table.table[i - 1].x;
|
||||||
|
double x1 = table.table[i].x;
|
||||||
|
if (x0 > x1)
|
||||||
|
boost::throw_exception(qi::expectation_failure<Iterator>(
|
||||||
|
table.table[i - 1].it_range_x.begin(), table.table[i].it_range_x.end(), spirit::info("X coordinates of the table must be increasing")));
|
||||||
|
if (! evaluated && x >= x0 && x <= x1) {
|
||||||
|
double y0 = table.table[i - 1].y;
|
||||||
|
double y1 = table.table[i].y;
|
||||||
|
if (x == x0)
|
||||||
|
out.set_d(y0);
|
||||||
|
else if (x == x1)
|
||||||
|
out.set_d(y1);
|
||||||
|
else if (is_approx(x0, x1))
|
||||||
|
out.set_d(0.5 * (y0 + y1));
|
||||||
|
else
|
||||||
|
out.set_d(Slic3r::lerp(y0, y1, (x - x0) / (x1 - x0)));
|
||||||
|
evaluated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! evaluated) {
|
||||||
|
// Clamp x into the table range with EPSILON.
|
||||||
|
if (x > table.table.front().x - EPSILON)
|
||||||
|
out.set_d(table.table.front().y);
|
||||||
|
else if (x < table.table.back().x + EPSILON)
|
||||||
|
out.set_d(table.table.back().y);
|
||||||
|
else
|
||||||
|
// The value is really outside the table range.
|
||||||
|
expr_x.throw_exception("Interpolation value is outside the table range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
std::ostream& operator<<(std::ostream &os, const InterpolateTableContext<Iterator> &table_context)
|
||||||
|
{
|
||||||
|
for (const auto &item : table_context.table)
|
||||||
|
os << "(" << item.x << "," << item.y << ")";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
// Table to translate symbol tag to a human readable error message.
|
// Table to translate symbol tag to a human readable error message.
|
||||||
std::map<std::string, std::string> MyContext::tag_to_error_message = {
|
std::map<std::string, std::string> MyContext::tag_to_error_message = {
|
||||||
{ "eoi", "Unknown syntax error" },
|
{ "eoi", "Unknown syntax error" },
|
||||||
@ -1428,6 +1492,7 @@ namespace client
|
|||||||
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
|
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
|
||||||
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ]
|
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ]
|
||||||
| (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ]
|
| (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ]
|
||||||
|
| (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ]
|
||||||
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
|
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
|
||||||
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
||||||
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
|
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
|
||||||
@ -1440,16 +1505,26 @@ namespace client
|
|||||||
one_of.name("one_of");
|
one_of.name("one_of");
|
||||||
one_of_list =
|
one_of_list =
|
||||||
eps[px::bind(&expr<Iterator>::one_of_test_init, _val)] >
|
eps[px::bind(&expr<Iterator>::one_of_test_init, _val)] >
|
||||||
( ',' > *(
|
( ( ',' > *(
|
||||||
(
|
(
|
||||||
unary_expression(_r1)[px::bind(&expr<Iterator>::template one_of_test<false>, _r2, _1, _val)]
|
unary_expression(_r1)[px::bind(&expr<Iterator>::template one_of_test<false>, _r2, _1, _val)]
|
||||||
| (lit('~') > unary_expression(_r1))[px::bind(&expr<Iterator>::template one_of_test<true>, _r2, _1, _val)]
|
| (lit('~') > unary_expression(_r1))[px::bind(&expr<Iterator>::template one_of_test<true>, _r2, _1, _val)]
|
||||||
| regular_expression[px::bind(&expr<Iterator>::one_of_test_regex, _r2, _1, _val)]
|
| regular_expression[px::bind(&expr<Iterator>::one_of_test_regex, _r2, _1, _val)]
|
||||||
) >> -lit(','))
|
) >> -lit(','))
|
||||||
|
)
|
||||||
| eps
|
| eps
|
||||||
);
|
);
|
||||||
one_of_list.name("one_of_list");
|
one_of_list.name("one_of_list");
|
||||||
|
|
||||||
|
interpolate_table = (unary_expression(_r1)[_a = _1] > ',' > interpolate_table_list(_r1, _a))
|
||||||
|
[px::bind(&InterpolateTableContext<Iterator>::evaluate, _a, _2, _val)];
|
||||||
|
interpolate_table.name("interpolate_table");
|
||||||
|
interpolate_table_list =
|
||||||
|
eps[px::bind(&InterpolateTableContext<Iterator>::init, _r2)] >
|
||||||
|
( *(( lit('(') > unary_expression(_r1) > ',' > unary_expression(_r1) > ')' )
|
||||||
|
[px::bind(&InterpolateTableContext<Iterator>::add_pair, _1, _2, _val)] >> -lit(',')) );
|
||||||
|
interpolate_table.name("interpolate_table_list");
|
||||||
|
|
||||||
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
|
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
|
||||||
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
||||||
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
||||||
@ -1486,6 +1561,7 @@ namespace client
|
|||||||
("elsif")
|
("elsif")
|
||||||
("endif")
|
("endif")
|
||||||
("false")
|
("false")
|
||||||
|
("interpolate_table")
|
||||||
("min")
|
("min")
|
||||||
("max")
|
("max")
|
||||||
("random")
|
("random")
|
||||||
@ -1501,9 +1577,12 @@ namespace client
|
|||||||
debug(text_block);
|
debug(text_block);
|
||||||
debug(macro);
|
debug(macro);
|
||||||
debug(if_else_output);
|
debug(if_else_output);
|
||||||
|
debug(interpolate_table);
|
||||||
// debug(switch_output);
|
// debug(switch_output);
|
||||||
debug(legacy_variable_expansion);
|
debug(legacy_variable_expansion);
|
||||||
debug(identifier);
|
debug(identifier);
|
||||||
|
debug(interpolate_table);
|
||||||
|
debug(interpolate_table_list);
|
||||||
debug(conditional_expression);
|
debug(conditional_expression);
|
||||||
debug(logical_or_expression);
|
debug(logical_or_expression);
|
||||||
debug(logical_and_expression);
|
debug(logical_and_expression);
|
||||||
@ -1569,6 +1648,9 @@ namespace client
|
|||||||
// Evaluating "one of" list of patterns.
|
// Evaluating "one of" list of patterns.
|
||||||
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> one_of;
|
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> one_of;
|
||||||
qi::rule<Iterator, expr<Iterator>(const MyContext*, const expr<Iterator> ¶m), spirit_encoding::space_type> one_of_list;
|
qi::rule<Iterator, expr<Iterator>(const MyContext*, const expr<Iterator> ¶m), spirit_encoding::space_type> one_of_list;
|
||||||
|
// Evaluating the "interpolate_table" expression.
|
||||||
|
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> interpolate_table;
|
||||||
|
qi::rule<Iterator, InterpolateTableContext<Iterator>(const MyContext*, const expr<Iterator> ¶m), spirit_encoding::space_type> interpolate_table_list;
|
||||||
|
|
||||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
|
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
|
||||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;
|
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;
|
||||||
|
@ -71,6 +71,9 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
|||||||
SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); }
|
SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); }
|
||||||
SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); }
|
SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); }
|
||||||
SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); }
|
SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); }
|
||||||
|
SECTION("math: interpolate_table(13.84375892476, (0, 0), (20, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13.84375892476, (0, 0), (20, 20))}")) == Approx(13.84375892476)); }
|
||||||
|
SECTION("math: interpolate_table(13, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13, (0, 0), (20, 20), (30, 20))}")) == Approx(13.)); }
|
||||||
|
SECTION("math: interpolate_table(25, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(25, (0, 0), (20, 20), (30, 20))}")) == Approx(20.)); }
|
||||||
|
|
||||||
// Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions.
|
// Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions.
|
||||||
// first_layer_extrusion_width ratio_over first_layer_heigth.
|
// first_layer_extrusion_width ratio_over first_layer_heigth.
|
||||||
|
Loading…
Reference in New Issue
Block a user