mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-05 19:46:18 +08:00
Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_show_sla_supports
This commit is contained in:
commit
5d40088c0e
@ -1086,6 +1086,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
m_placeholder_parser = print.placeholder_parser();
|
||||
m_placeholder_parser.update_timestamp();
|
||||
m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||
// Enable passing global variables between PlaceholderParser invocations.
|
||||
m_placeholder_parser_context.global_config = std::make_unique<DynamicConfig>();
|
||||
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
|
||||
|
||||
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
||||
|
@ -191,6 +191,10 @@ namespace client
|
||||
struct expr
|
||||
{
|
||||
expr() {}
|
||||
expr(const expr &rhs) : m_type(rhs.type()), it_range(rhs.it_range)
|
||||
{ if (rhs.type() == TYPE_STRING) m_data.s = new std::string(*rhs.m_data.s); else m_data.set(rhs.m_data); }
|
||||
expr(expr &&rhs) : expr(std::move(rhs), rhs.it_range.begin(), rhs.it_range.end()) {}
|
||||
|
||||
explicit expr(bool b) : m_type(TYPE_BOOL) { m_data.b = b; }
|
||||
explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_BOOL), it_range(it_begin, it_end) { m_data.b = b; }
|
||||
explicit expr(int i) : m_type(TYPE_INT) { m_data.i = i; }
|
||||
@ -201,9 +205,6 @@ namespace client
|
||||
explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
|
||||
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) :
|
||||
m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); }
|
||||
expr(const expr &rhs) : m_type(rhs.type()), it_range(rhs.it_range)
|
||||
{ if (rhs.type() == TYPE_STRING) m_data.s = new std::string(*rhs.m_data.s); else m_data.set(rhs.m_data); }
|
||||
explicit expr(expr &&rhs) : expr(rhs, rhs.it_range.begin(), rhs.it_range.end()) {}
|
||||
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end }
|
||||
{
|
||||
m_data.set(rhs.m_data);
|
||||
@ -249,6 +250,7 @@ namespace client
|
||||
TYPE_STRING,
|
||||
};
|
||||
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() const { return m_data.b; }
|
||||
@ -472,8 +474,7 @@ namespace client
|
||||
static void compare_op(expr &lhs, expr &rhs, char op, bool invert)
|
||||
{
|
||||
bool value = false;
|
||||
if ((lhs.type() == TYPE_INT || lhs.type() == TYPE_DOUBLE) &&
|
||||
(rhs.type() == TYPE_INT || rhs.type() == TYPE_DOUBLE)) {
|
||||
if (lhs.numeric_type() && rhs.numeric_type()) {
|
||||
// Both types are numeric.
|
||||
switch (op) {
|
||||
case '=':
|
||||
@ -681,7 +682,7 @@ namespace client
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -723,7 +724,10 @@ namespace client
|
||||
const DynamicConfig *config = nullptr;
|
||||
const DynamicConfig *config_override = nullptr;
|
||||
mutable DynamicConfig *config_outputs = nullptr;
|
||||
// Local variables, read / write
|
||||
mutable DynamicConfig config_local;
|
||||
size_t current_extruder_id = 0;
|
||||
// Random number generator and optionally global variables.
|
||||
PlaceholderParser::ContextData *context_data = nullptr;
|
||||
// If false, the macro_processor will evaluate a full macro.
|
||||
// If true, the macro processor will evaluate just a boolean condition using the full expressive power of the macro processor.
|
||||
@ -733,7 +737,7 @@ namespace client
|
||||
// Table to translate symbol tag to a human readable error message.
|
||||
static std::map<std::string, std::string> tag_to_error_message;
|
||||
|
||||
static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; }
|
||||
static bool evaluate_full_macro(const MyContext *ctx) { return ! ctx->just_boolean_expression; }
|
||||
|
||||
const ConfigOption* optptr(const t_config_option_key &opt_key) const override
|
||||
{
|
||||
@ -748,7 +752,24 @@ namespace client
|
||||
}
|
||||
|
||||
const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); }
|
||||
ConfigOption* resolve_output_symbol(const std::string &opt_key) const { return this->config_outputs ? this->config_outputs->optptr(opt_key, false) : nullptr; }
|
||||
ConfigOption* resolve_output_symbol(const std::string &opt_key) const {
|
||||
ConfigOption *out = nullptr;
|
||||
if (this->config_outputs)
|
||||
out = this->config_outputs->optptr(opt_key, false);
|
||||
if (out == nullptr && this->context_data != nullptr && this->context_data->global_config)
|
||||
out = this->context_data->global_config->optptr(opt_key);
|
||||
if (out == nullptr)
|
||||
out = this->config_local.optptr(opt_key);
|
||||
return out;
|
||||
}
|
||||
void store_new_variable(const std::string &opt_key, ConfigOption *opt, bool global_variable) {
|
||||
assert(opt != nullptr);
|
||||
if (global_variable) {
|
||||
assert(this->context_data != nullptr && this->context_data->global_config);
|
||||
this->context_data->global_config->set_key_value(opt_key, opt);
|
||||
} else
|
||||
this->config_local.set_key_value(opt_key ,opt);
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void legacy_variable_expansion(
|
||||
@ -953,17 +974,105 @@ namespace client
|
||||
output.it_range = opt.it_range;
|
||||
}
|
||||
|
||||
// Decoding a scalar variable symbol "opt", assigning it a value of "param".
|
||||
template<typename Iterator>
|
||||
struct NewOldVariable {
|
||||
std::string name;
|
||||
boost::iterator_range<Iterator> it_range;
|
||||
ConfigOption *opt{ nullptr };
|
||||
};
|
||||
template <typename Iterator>
|
||||
static void variable_assign(
|
||||
static void new_old_variable(
|
||||
const MyContext *ctx,
|
||||
OptWithPos<Iterator> &opt,
|
||||
expr<Iterator> ¶m,
|
||||
// Not used, just clear it.
|
||||
std::string &out)
|
||||
bool global_variable,
|
||||
const boost::iterator_range<Iterator> &it_range,
|
||||
NewOldVariable<Iterator> &out)
|
||||
{
|
||||
t_config_option_key key(std::string(it_range.begin(), it_range.end()));
|
||||
if (const ConfigOption* opt = ctx->resolve_symbol(key); opt)
|
||||
ctx->throw_exception("Symbol is already defined in read-only system dictionary", it_range);
|
||||
if (ctx->config_outputs && ctx->config_outputs->optptr(key))
|
||||
ctx->throw_exception("Symbol is already defined as system output variable", it_range);
|
||||
|
||||
bool has_global_dictionary = ctx->context_data != nullptr && ctx->context_data->global_config;
|
||||
if (global_variable) {
|
||||
if (! has_global_dictionary)
|
||||
ctx->throw_exception("Global variables are not available in this context", it_range);
|
||||
if (ctx->config_local.optptr(key))
|
||||
ctx->throw_exception("Variable name already defined in local scope", it_range);
|
||||
out.opt = ctx->context_data->global_config->optptr(key);
|
||||
} else {
|
||||
if (has_global_dictionary && ctx->context_data->global_config->optptr(key))
|
||||
ctx->throw_exception("Variable name already defined in global scope", it_range);
|
||||
out.opt = ctx->config_local.optptr(key);
|
||||
}
|
||||
|
||||
out.name = std::move(key);
|
||||
out.it_range = it_range;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void new_scalar_variable(
|
||||
const MyContext *ctx,
|
||||
bool global_variable,
|
||||
NewOldVariable<Iterator> &output_variable,
|
||||
const expr<Iterator> ¶m)
|
||||
{
|
||||
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||
if (! param.numeric_type())
|
||||
param.throw_exception("Right side is not a numeric expression");
|
||||
};
|
||||
if (output_variable.opt) {
|
||||
if (output_variable.opt->is_vector())
|
||||
param.throw_exception("Cannot assign a scalar value to a vector variable.");
|
||||
switch (output_variable.opt->type()) {
|
||||
case coFloat:
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionFloat*>(output_variable.opt)->value = param.as_d();
|
||||
break;
|
||||
case coInt:
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionInt*>(output_variable.opt)->value = param.as_i();
|
||||
break;
|
||||
case coString:
|
||||
static_cast<ConfigOptionString*>(output_variable.opt)->value = param.to_string();
|
||||
break;
|
||||
case coBool:
|
||||
if (param.type() != expr<Iterator>::TYPE_BOOL)
|
||||
param.throw_exception("Right side is not a boolean expression");
|
||||
static_cast<ConfigOptionBool*>(output_variable.opt)->value = param.b();
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
} else {
|
||||
switch (param.type()) {
|
||||
case expr<Iterator>::TYPE_BOOL: output_variable.opt = new ConfigOptionBool(param.b()); break;
|
||||
case expr<Iterator>::TYPE_INT: output_variable.opt = new ConfigOptionInt(param.i()); break;
|
||||
case expr<Iterator>::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloat(param.d()); break;
|
||||
case expr<Iterator>::TYPE_STRING: output_variable.opt = new ConfigOptionString(param.s()); break;
|
||||
default: assert(false);
|
||||
}
|
||||
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void check_writable(const MyContext *ctx, OptWithPos<Iterator> &opt) {
|
||||
if (! opt.writable)
|
||||
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
||||
}
|
||||
|
||||
// Decoding a scalar variable symbol "opt", assigning it a value of "param".
|
||||
template <typename Iterator>
|
||||
static void assign_scalar_variable(
|
||||
const MyContext *ctx,
|
||||
OptWithPos<Iterator> &opt,
|
||||
expr<Iterator> ¶m)
|
||||
{
|
||||
check_writable(ctx, opt);
|
||||
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.has_index())
|
||||
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
|
||||
@ -974,21 +1083,18 @@ namespace client
|
||||
ctx->throw_exception("Index out of range", opt.it_range);
|
||||
switch (opt.opt->type()) {
|
||||
case coFloats:
|
||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
|
||||
break;
|
||||
case coInts:
|
||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
|
||||
break;
|
||||
case coStrings:
|
||||
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
|
||||
break;
|
||||
case coPercents:
|
||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
|
||||
break;
|
||||
case coBools:
|
||||
@ -1004,21 +1110,18 @@ namespace client
|
||||
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
|
||||
switch (wropt->type()) {
|
||||
case coFloat:
|
||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
|
||||
break;
|
||||
case coInt:
|
||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
|
||||
break;
|
||||
case coString:
|
||||
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
|
||||
break;
|
||||
case coPercent:
|
||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||
check_numeric(param);
|
||||
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
|
||||
break;
|
||||
case coBool:
|
||||
@ -1030,7 +1133,363 @@ namespace client
|
||||
ctx->throw_exception("Unsupported output scalar variable type", opt.it_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void new_vector_variable_array(
|
||||
const MyContext *ctx,
|
||||
bool global_variable,
|
||||
NewOldVariable<Iterator> &output_variable,
|
||||
const expr<Iterator> &expr_count,
|
||||
const expr<Iterator> &expr_value)
|
||||
{
|
||||
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||
if (! param.numeric_type())
|
||||
param.throw_exception("Right side is not a numeric expression");
|
||||
};
|
||||
auto evaluate_count = [](const expr<Iterator> &expr_count) -> size_t {
|
||||
if (expr_count.type() != expr<Iterator>::TYPE_INT)
|
||||
expr_count.throw_exception("Expected number of elements to fill a vector with.");
|
||||
int count = expr_count.i();
|
||||
if (count < 0)
|
||||
expr_count.throw_exception("Negative number of elements specified.");
|
||||
return size_t(count);
|
||||
};
|
||||
if (output_variable.opt) {
|
||||
if (output_variable.opt->is_scalar())
|
||||
expr_value.throw_exception("Cannot assign a vector value to a scalar variable.");
|
||||
size_t count = evaluate_count(expr_count);
|
||||
switch (output_variable.opt->type()) {
|
||||
case coFloats:
|
||||
check_numeric(expr_value);
|
||||
static_cast<ConfigOptionFloats*>(output_variable.opt)->values.assign(count, expr_value.as_d());
|
||||
break;
|
||||
case coInts:
|
||||
check_numeric(expr_value);
|
||||
static_cast<ConfigOptionInts*>(output_variable.opt)->values.assign(count, expr_value.as_i());
|
||||
break;
|
||||
case coStrings:
|
||||
static_cast<ConfigOptionStrings*>(output_variable.opt)->values.assign(count, expr_value.to_string());
|
||||
break;
|
||||
case coBools:
|
||||
if (expr_value.type() != expr<Iterator>::TYPE_BOOL)
|
||||
expr_value.throw_exception("Right side is not a boolean expression");
|
||||
static_cast<ConfigOptionBools*>(output_variable.opt)->values.assign(count, expr_value.b());
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
} else {
|
||||
size_t count = evaluate_count(expr_count);
|
||||
switch (expr_value.type()) {
|
||||
case expr<Iterator>::TYPE_BOOL: output_variable.opt = new ConfigOptionBools(count, expr_value.b()); break;
|
||||
case expr<Iterator>::TYPE_INT: output_variable.opt = new ConfigOptionInts(count, expr_value.i()); break;
|
||||
case expr<Iterator>::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloats(count, expr_value.d()); break;
|
||||
case expr<Iterator>::TYPE_STRING: output_variable.opt = new ConfigOptionStrings(count, expr_value.s()); break;
|
||||
default: assert(false);
|
||||
}
|
||||
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void assign_vector_variable_array(
|
||||
const MyContext *ctx,
|
||||
OptWithPos<Iterator> &lhs,
|
||||
const expr<Iterator> &expr_count,
|
||||
const expr<Iterator> &expr_value)
|
||||
{
|
||||
check_writable(ctx, lhs);
|
||||
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||
if (! param.numeric_type())
|
||||
param.throw_exception("Right side is not a numeric expression");
|
||||
};
|
||||
auto evaluate_count = [](const expr<Iterator> &expr_count) -> size_t {
|
||||
if (expr_count.type() != expr<Iterator>::TYPE_INT)
|
||||
expr_count.throw_exception("Expected number of elements to fill a vector with.");
|
||||
int count = expr_count.i();
|
||||
if (count < 0)
|
||||
expr_count.throw_exception("Negative number of elements specified.");
|
||||
return size_t(count);
|
||||
};
|
||||
if (lhs.opt->is_scalar())
|
||||
expr_value.throw_exception("Cannot assign a vector value to a scalar variable.");
|
||||
auto *opt = const_cast<ConfigOption*>(lhs.opt);
|
||||
size_t count = evaluate_count(expr_count);
|
||||
switch (lhs.opt->type()) {
|
||||
case coFloats:
|
||||
check_numeric(expr_value);
|
||||
static_cast<ConfigOptionFloats*>(opt)->values.assign(count, expr_value.as_d());
|
||||
break;
|
||||
case coInts:
|
||||
check_numeric(expr_value);
|
||||
static_cast<ConfigOptionInts*>(opt)->values.assign(count, expr_value.as_i());
|
||||
break;
|
||||
case coStrings:
|
||||
static_cast<ConfigOptionStrings*>(opt)->values.assign(count, expr_value.to_string());
|
||||
break;
|
||||
case coBools:
|
||||
if (expr_value.type() != expr<Iterator>::TYPE_BOOL)
|
||||
expr_value.throw_exception("Right side is not a boolean expression");
|
||||
static_cast<ConfigOptionBools*>(opt)->values.assign(count, expr_value.b());
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void new_vector_variable_initializer_list(
|
||||
const MyContext *ctx,
|
||||
bool global_variable,
|
||||
NewOldVariable<Iterator> &output_variable,
|
||||
const std::vector<expr<Iterator>> &il)
|
||||
{
|
||||
if (! output_variable.opt) {
|
||||
// First guesstimate type of the output vector.
|
||||
size_t num_bool = 0;
|
||||
size_t num_int = 0;
|
||||
size_t num_double = 0;
|
||||
size_t num_string = 0;
|
||||
for (auto &i : il)
|
||||
switch (i.type()) {
|
||||
case expr<Iterator>::TYPE_BOOL: ++ num_bool; break;
|
||||
case expr<Iterator>::TYPE_INT: ++ num_int; break;
|
||||
case expr<Iterator>::TYPE_DOUBLE: ++ num_double; break;
|
||||
case expr<Iterator>::TYPE_STRING: ++ num_string; break;
|
||||
default: assert(false);
|
||||
}
|
||||
if (num_string > 0)
|
||||
// Convert everything to strings.
|
||||
output_variable.opt = new ConfigOptionStrings();
|
||||
else if (num_bool > 0) {
|
||||
if (num_double + num_int > 0)
|
||||
ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range<Iterator>{ il.front().it_range.begin(), il.back().it_range.end() });
|
||||
output_variable.opt = new ConfigOptionBools();
|
||||
} else
|
||||
// Output is numeric.
|
||||
output_variable.opt = num_double == 0 ? static_cast<ConfigOption*>(new ConfigOptionInts()) : static_cast<ConfigOption*>(new ConfigOptionFloats());
|
||||
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||
}
|
||||
|
||||
auto check_numeric = [](const std::vector<expr<Iterator>> &il) {
|
||||
for (auto& i : il)
|
||||
if (!i.numeric_type())
|
||||
i.throw_exception("Right side is not a numeric expression");
|
||||
};
|
||||
|
||||
if (output_variable.opt->is_scalar())
|
||||
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", output_variable.it_range);
|
||||
|
||||
switch (output_variable.opt->type()) {
|
||||
case coFloats:
|
||||
{
|
||||
check_numeric(il);
|
||||
auto &out = static_cast<ConfigOptionFloats*>(output_variable.opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto &i : il)
|
||||
out.emplace_back(i.as_d());
|
||||
break;
|
||||
}
|
||||
case coInts:
|
||||
{
|
||||
check_numeric(il);
|
||||
auto &out = static_cast<ConfigOptionInts*>(output_variable.opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto& i : il)
|
||||
out.emplace_back(i.as_i());
|
||||
break;
|
||||
}
|
||||
case coStrings:
|
||||
{
|
||||
auto &out = static_cast<ConfigOptionStrings*>(output_variable.opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto &i : il)
|
||||
out.emplace_back(i.to_string());
|
||||
break;
|
||||
}
|
||||
case coBools:
|
||||
{
|
||||
auto &out = static_cast<ConfigOptionBools*>(output_variable.opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto &i : il)
|
||||
if (i.type() == expr<Iterator>::TYPE_BOOL)
|
||||
out.emplace_back(i.b());
|
||||
else
|
||||
i.throw_exception("Right side is not a boolean expression");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void assign_vector_variable_initializer_list(
|
||||
const MyContext *ctx,
|
||||
OptWithPos<Iterator> &lhs,
|
||||
const std::vector<expr<Iterator>> &il)
|
||||
{
|
||||
check_writable(ctx, lhs);
|
||||
auto check_numeric = [](const std::vector<expr<Iterator>> &il) {
|
||||
for (auto &i : il)
|
||||
if (! i.numeric_type())
|
||||
i.throw_exception("Right side is not a numeric expression");
|
||||
};
|
||||
|
||||
if (lhs.opt->is_scalar())
|
||||
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
|
||||
|
||||
ConfigOption *opt = const_cast<ConfigOption*>(lhs.opt);
|
||||
switch (lhs.opt->type()) {
|
||||
case coFloats:
|
||||
{
|
||||
check_numeric(il);
|
||||
auto &out = static_cast<ConfigOptionFloats*>(opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto &i : il)
|
||||
out.emplace_back(i.as_d());
|
||||
break;
|
||||
}
|
||||
case coInts:
|
||||
{
|
||||
check_numeric(il);
|
||||
auto &out = static_cast<ConfigOptionInts*>(opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto& i : il)
|
||||
out.emplace_back(i.as_i());
|
||||
break;
|
||||
}
|
||||
case coStrings:
|
||||
{
|
||||
auto &out = static_cast<ConfigOptionStrings*>(opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto &i : il)
|
||||
out.emplace_back(i.to_string());
|
||||
break;
|
||||
}
|
||||
case coBools:
|
||||
{
|
||||
auto &out = static_cast<ConfigOptionBools*>(opt)->values;
|
||||
out.clear();
|
||||
out.reserve(il.size());
|
||||
for (auto &i : il)
|
||||
if (i.type() == expr<Iterator>::TYPE_BOOL)
|
||||
out.emplace_back(i.b());
|
||||
else
|
||||
i.throw_exception("Right side is not a boolean expression");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static bool new_vector_variable_copy(
|
||||
const MyContext *ctx,
|
||||
bool global_variable,
|
||||
NewOldVariable<Iterator> &output_variable,
|
||||
const OptWithPos<Iterator> &src_variable)
|
||||
{
|
||||
if (! is_vector_variable_reference(src_variable))
|
||||
// Skip parsing this branch, bactrack.
|
||||
return false;
|
||||
|
||||
if (! output_variable.opt) {
|
||||
if (one_of(src_variable.opt->type(), { coFloats, coInts, coStrings, coBools }))
|
||||
output_variable.opt = src_variable.opt->clone();
|
||||
else if (src_variable.opt->type() == coPercents)
|
||||
output_variable.opt = new ConfigOptionFloats(static_cast<const ConfigOptionPercents*>(src_variable.opt)->values);
|
||||
else
|
||||
ctx->throw_exception("Duplicating this vector variable is not supported", src_variable.it_range);
|
||||
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||
}
|
||||
|
||||
switch (output_variable.opt->type()) {
|
||||
case coFloats:
|
||||
if (output_variable.opt->type() != coFloats)
|
||||
ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||
static_cast<ConfigOptionFloats*>(output_variable.opt)->values = static_cast<const ConfigOptionFloats*>(src_variable.opt)->values;
|
||||
break;
|
||||
case coInts:
|
||||
if (output_variable.opt->type() != coInts)
|
||||
ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||
static_cast<ConfigOptionInts*>(output_variable.opt)->values = static_cast<const ConfigOptionInts*>(src_variable.opt)->values;
|
||||
break;
|
||||
case coStrings:
|
||||
if (output_variable.opt->type() != coStrings)
|
||||
ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||
static_cast<ConfigOptionStrings*>(output_variable.opt)->values = static_cast<const ConfigOptionStrings*>(src_variable.opt)->values;
|
||||
break;
|
||||
case coBools:
|
||||
if (output_variable.opt->type() != coBools)
|
||||
ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||
static_cast<ConfigOptionBools*>(output_variable.opt)->values = static_cast<const ConfigOptionBools*>(src_variable.opt)->values;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
// Continue parsing.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static bool is_vector_variable_reference(const OptWithPos<Iterator> &var) {
|
||||
return ! var.has_index() && var.opt->is_vector();
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static bool assign_vector_variable_copy(
|
||||
const MyContext *ctx,
|
||||
OptWithPos<Iterator> &lhs,
|
||||
const OptWithPos<Iterator> &src_variable)
|
||||
{
|
||||
if (! is_vector_variable_reference(src_variable))
|
||||
// Skip parsing this branch, bactrack.
|
||||
return false;
|
||||
|
||||
check_writable(ctx, lhs);
|
||||
|
||||
auto *opt = const_cast<ConfigOption*>(lhs.opt);
|
||||
switch (lhs.opt->type()) {
|
||||
case coFloats:
|
||||
if (lhs.opt->type() != coFloats)
|
||||
ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range);
|
||||
static_cast<ConfigOptionFloats*>(opt)->values = static_cast<const ConfigOptionFloats*>(src_variable.opt)->values;
|
||||
break;
|
||||
case coInts:
|
||||
if (lhs.opt->type() != coInts)
|
||||
ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range);
|
||||
static_cast<ConfigOptionInts*>(opt)->values = static_cast<const ConfigOptionInts*>(src_variable.opt)->values;
|
||||
break;
|
||||
case coStrings:
|
||||
if (lhs.opt->type() != coStrings)
|
||||
ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range);
|
||||
static_cast<ConfigOptionStrings*>(opt)->values = static_cast<const ConfigOptionStrings*>(src_variable.opt)->values;
|
||||
break;
|
||||
case coBools:
|
||||
if (lhs.opt->type() != coBools)
|
||||
ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range);
|
||||
static_cast<ConfigOptionBools*>(opt)->values = static_cast<const ConfigOptionBools*>(src_variable.opt)->values;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Continue parsing.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void new_vector_variable_initializer_list_append(std::vector<expr<Iterator>> &list, expr<Iterator> &expr)
|
||||
{
|
||||
list.emplace_back(std::move(expr));
|
||||
}
|
||||
|
||||
// Verify that the expression returns an integer, which may be used
|
||||
@ -1109,8 +1568,74 @@ namespace client
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct InterpolateTableContext {
|
||||
struct Item {
|
||||
double x;
|
||||
boost::iterator_range<Iterator> it_range_x;
|
||||
double y;
|
||||
};
|
||||
std::vector<Item> 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.
|
||||
std::map<std::string, std::string> MyContext::tag_to_error_message = {
|
||||
{ "array", "Unknown syntax error" },
|
||||
{ "eoi", "Unknown syntax error" },
|
||||
{ "start", "Unknown syntax error" },
|
||||
{ "text", "Invalid text." },
|
||||
@ -1259,8 +1784,8 @@ namespace client
|
||||
// Also the start symbol switches between the "full macro syntax" and a "boolean expression only",
|
||||
// depending on the context->just_boolean_expression flag. This way a single static expression parser
|
||||
// could serve both purposes.
|
||||
start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] >
|
||||
( (eps(_a==true) > text_block(_r1) [_val=_1])
|
||||
start =
|
||||
( (eps(px::bind(&MyContext::evaluate_full_macro, _r1)) > text_block(_r1) [_val=_1])
|
||||
| conditional_expression(_r1) [ px::bind(&expr<Iterator>::evaluate_boolean_to_string, _1, _val) ]
|
||||
) > eoi;
|
||||
start.name("start");
|
||||
@ -1271,7 +1796,7 @@ namespace client
|
||||
// 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('{') >> macro(_r1)[_val+=_1] > *(+lit(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}')
|
||||
| (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')
|
||||
);
|
||||
text_block.name("text_block");
|
||||
@ -1287,6 +1812,7 @@ namespace client
|
||||
(kw["if"] > if_else_output(_r1) [_val = _1])
|
||||
// | (kw["switch"] > switch_output(_r1) [_val = _1])
|
||||
| (assignment_statement(_r1) [_val = _1])
|
||||
| (new_variable_statement(_r1) [_val = _1])
|
||||
| (additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ])
|
||||
;
|
||||
macro.name("macro");
|
||||
@ -1381,8 +1907,39 @@ namespace client
|
||||
multiplicative_expression.name("multiplicative_expression");
|
||||
|
||||
assignment_statement =
|
||||
(variable_reference(_r1) >> '=' > additive_expression(_r1))
|
||||
[px::bind(&MyContext::variable_assign<Iterator>, _r1, _1, _2, _val)];
|
||||
variable_reference(_r1)[_a = _1] >> '=' >
|
||||
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
|
||||
(lit('(') > new_variable_initializer_list(_r1) > ')')
|
||||
[px::bind(&MyContext::assign_vector_variable_initializer_list<Iterator>, _r1, _a, _1)]
|
||||
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
|
||||
// Only process such variable references, which return a naked vector variable.
|
||||
| variable_reference(_r1)
|
||||
[px::ref(qi::_pass) = px::bind(&MyContext::assign_vector_variable_copy<Iterator>, _r1, _a, _1)]
|
||||
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
|
||||
| conditional_expression(_r1)
|
||||
[px::bind(&MyContext::assign_scalar_variable<Iterator>, _r1, _a, _1)]
|
||||
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
|
||||
[px::bind(&MyContext::assign_vector_variable_array<Iterator>, _r1, _a, _1, _2)]
|
||||
);
|
||||
|
||||
new_variable_statement =
|
||||
(kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable<Iterator>, _r1, _a, _1, _b)] > lit('=') >
|
||||
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
|
||||
(lit('(') > new_variable_initializer_list(_r1) > ')')
|
||||
[px::bind(&MyContext::new_vector_variable_initializer_list<Iterator>, _r1, _a, _b, _1)]
|
||||
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
|
||||
// Only process such variable references, which return a naked vector variable.
|
||||
| variable_reference(_r1)
|
||||
[px::ref(qi::_pass) = px::bind(&MyContext::new_vector_variable_copy<Iterator>, _r1, _a, _b, _1)]
|
||||
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
|
||||
| conditional_expression(_r1)
|
||||
[px::bind(&MyContext::new_scalar_variable<Iterator>, _r1, _a, _b, _1)]
|
||||
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
|
||||
[px::bind(&MyContext::new_vector_variable_array<Iterator>, _r1, _a, _b, _1, _2)]
|
||||
);
|
||||
new_variable_initializer_list =
|
||||
conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append<Iterator>, _val, _1)] >>
|
||||
*(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append<Iterator>, _val, _1)]);
|
||||
|
||||
struct FactorActions {
|
||||
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)
|
||||
@ -1428,6 +1985,7 @@ namespace client
|
||||
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
|
||||
| (kw["is_nil"] > '(' > is_nil_test(_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) ]
|
||||
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
||||
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
|
||||
@ -1440,16 +1998,26 @@ namespace client
|
||||
one_of.name("one_of");
|
||||
one_of_list =
|
||||
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)]
|
||||
| (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)]
|
||||
) >> -lit(','))
|
||||
)
|
||||
| eps
|
||||
);
|
||||
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)] >> (
|
||||
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
||||
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
||||
@ -1469,23 +2037,27 @@ namespace client
|
||||
variable_reference.name("variable reference");
|
||||
|
||||
variable = identifier[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
|
||||
variable.name("variable reference");
|
||||
variable.name("variable name");
|
||||
|
||||
regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']];
|
||||
regular_expression.name("regular_expression");
|
||||
|
||||
keywords.add
|
||||
("and")
|
||||
("array")
|
||||
("digits")
|
||||
("zdigits")
|
||||
("if")
|
||||
("int")
|
||||
("is_nil")
|
||||
("local")
|
||||
//("inf")
|
||||
("else")
|
||||
("elsif")
|
||||
("endif")
|
||||
("false")
|
||||
("global")
|
||||
("interpolate_table")
|
||||
("min")
|
||||
("max")
|
||||
("random")
|
||||
@ -1501,9 +2073,12 @@ namespace client
|
||||
debug(text_block);
|
||||
debug(macro);
|
||||
debug(if_else_output);
|
||||
debug(interpolate_table);
|
||||
// debug(switch_output);
|
||||
debug(legacy_variable_expansion);
|
||||
debug(identifier);
|
||||
debug(interpolate_table);
|
||||
debug(interpolate_table_list);
|
||||
debug(conditional_expression);
|
||||
debug(logical_or_expression);
|
||||
debug(logical_and_expression);
|
||||
@ -1569,11 +2144,17 @@ namespace client
|
||||
// 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*, 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<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;
|
||||
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>>, spirit_encoding::space_type> assignment_statement;
|
||||
// Allocating new local or global variables.
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable<Iterator>>, spirit_encoding::space_type> new_variable_statement;
|
||||
qi::rule<Iterator, std::vector<expr<Iterator>>(const MyContext*), spirit_encoding::space_type> new_variable_initializer_list;
|
||||
|
||||
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
|
||||
qi::symbols<char> keywords;
|
||||
};
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ public:
|
||||
// and shared between the PlaceholderParser::process() invocations.
|
||||
struct ContextData {
|
||||
std::mt19937 rng;
|
||||
// If defined, then this dictionary is used by the scripts to define user variables and persist them
|
||||
// between PlaceholderParser evaluations.
|
||||
std::unique_ptr<DynamicConfig> global_config;
|
||||
};
|
||||
|
||||
PlaceholderParser(const DynamicConfig *external_config = nullptr);
|
||||
|
@ -528,7 +528,8 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
|
||||
its_transform(mesh, to_octree * this->trafo_centered(), true);
|
||||
|
||||
// Triangulate internal bridging surfaces.
|
||||
std::vector<std::vector<Vec3d>> overhangs(surfaces_w_bottom_z.size());
|
||||
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1)));
|
||||
// ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()),
|
||||
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
|
||||
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "WxFontUtils.hpp"
|
||||
#include <boost/assign.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CoreText/CTFont.h>
|
||||
@ -14,10 +15,9 @@
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef __APPLE__
|
||||
static bool is_valid_ttf(std::string_view file_path)
|
||||
namespace {
|
||||
bool is_valid_ttf(std::string_view file_path)
|
||||
{
|
||||
if (file_path.empty()) return false;
|
||||
auto const pos_point = file_path.find_last_of('.');
|
||||
@ -34,8 +34,7 @@ static bool is_valid_ttf(std::string_view file_path)
|
||||
if (extension_size >= 5) return false; // a lot of symbols for extension
|
||||
if (extension_size <= 1) return false; // few letters for extension
|
||||
|
||||
std::string_view extension = file_path.substr(pos_point + 1,
|
||||
extension_size);
|
||||
std::string_view extension = file_path.substr(pos_point + 1, extension_size);
|
||||
|
||||
// Because of MacOs - Courier, Geneva, Monaco
|
||||
if (extension == std::string_view("dfont")) return false;
|
||||
@ -44,28 +43,29 @@ static bool is_valid_ttf(std::string_view file_path)
|
||||
}
|
||||
|
||||
// get filepath from wxFont on Mac OsX
|
||||
static std::string get_file_path(const wxFont& font) {
|
||||
std::string get_file_path(const wxFont& font) {
|
||||
const wxNativeFontInfo *info = font.GetNativeFontInfo();
|
||||
if (info == nullptr) return {};
|
||||
CTFontDescriptorRef descriptor = info->GetCTFontDescriptor();
|
||||
CFURLRef typeref = (CFURLRef)
|
||||
CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
|
||||
CFURLRef typeref = (CFURLRef)CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
|
||||
if (typeref == NULL) return {};
|
||||
ScopeGuard sg([&typeref]() { CFRelease(typeref); });
|
||||
CFStringRef url = CFURLGetString(typeref);
|
||||
if (url == NULL) return {};
|
||||
wxString file_uri;
|
||||
wxCFTypeRef(url).GetValue(file_uri);
|
||||
wxString file_uri(wxCFStringRef::AsString(url));
|
||||
wxURI uri(file_uri);
|
||||
const wxString &path = uri.GetPath();
|
||||
std::string path_str(wxURI::Unescape(path).c_str());
|
||||
wxString path_unescaped = wxURI::Unescape(path);
|
||||
std::string path_str = path_unescaped.ToUTF8().data();
|
||||
BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ").";
|
||||
return path_str;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
} // namespace
|
||||
#endif // __APPLE__
|
||||
|
||||
bool WxFontUtils::can_load(const wxFont &font)
|
||||
{
|
||||
|
||||
if (!font.IsOk()) return false;
|
||||
#ifdef _WIN32
|
||||
return Emboss::can_load(font.GetHFONT()) != nullptr;
|
||||
|
@ -258,4 +258,16 @@ SCENARIO("Custom G-code", "[CustomGCode]")
|
||||
REQUIRE(match_count == 2);
|
||||
}
|
||||
}
|
||||
GIVEN("before_layer_gcode increments global variable") {
|
||||
auto config = Slic3r::DynamicPrintConfig::new_with({
|
||||
{ "start_gcode", "{global counter=0}" },
|
||||
{ "before_layer_gcode", ";Counter{counter=counter+1;counter}\n" }
|
||||
});
|
||||
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config);
|
||||
THEN("The counter is emitted multiple times before layer change.") {
|
||||
REQUIRE(Slic3r::Test::contains(gcode, ";Counter1\n"));
|
||||
REQUIRE(Slic3r::Test::contains(gcode, ";Counter2\n"));
|
||||
REQUIRE(Slic3r::Test::contains(gcode, ";Counter3\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,10 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
||||
SECTION("nullable is not null") { REQUIRE(parser.process("{is_nil(filament_retract_length[0])}") == "false"); }
|
||||
SECTION("nullable is null") { REQUIRE(parser.process("{is_nil(filament_retract_length[1])}") == "true"); }
|
||||
SECTION("nullable is not null 2") { REQUIRE(parser.process("{is_nil(filament_retract_length[2])}") == "false"); }
|
||||
SECTION("multiple expressions") { REQUIRE(parser.process("{temperature[foo];temperature[foo]}") == "357357"); }
|
||||
SECTION("multiple expressions with semicolons") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];}") == "357357"); }
|
||||
SECTION("multiple expressions with semicolons 2") { REQUIRE(parser.process("{temperature[foo];;temperature[foo];}") == "357357"); }
|
||||
SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); }
|
||||
|
||||
// Test the math expressions.
|
||||
SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); }
|
||||
@ -71,6 +75,9 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
||||
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: 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.
|
||||
// first_layer_extrusion_width ratio_over first_layer_heigth.
|
||||
@ -143,3 +150,76 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
||||
REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.));
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
|
||||
PlaceholderParser parser;
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
|
||||
config.set_deserialize_strict({
|
||||
{ "filament_notes", "testnotes" },
|
||||
{ "enable_dynamic_fan_speeds", "1" },
|
||||
{ "nozzle_diameter", "0.6;0.6;0.6;0.6" },
|
||||
{ "temperature", "357;359;363;378" }
|
||||
});
|
||||
|
||||
PlaceholderParser::ContextData context_with_global_dict;
|
||||
context_with_global_dict.global_config = std::make_unique<DynamicConfig>();
|
||||
|
||||
SECTION("create an int local variable") { REQUIRE(parser.process("{local myint = 33+2}{myint}", 0, nullptr, nullptr, nullptr) == "35"); }
|
||||
SECTION("create a string local variable") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
|
||||
SECTION("create a bool local variable") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, nullptr) == "true"); }
|
||||
SECTION("create an int global variable") { REQUIRE(parser.process("{global myint = 33+2}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "35"); }
|
||||
SECTION("create a string global variable") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
|
||||
SECTION("create a bool global variable") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
|
||||
|
||||
SECTION("create an int local variable and overwrite it") { REQUIRE(parser.process("{local myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); }
|
||||
SECTION("create a string local variable and overwrite it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); }
|
||||
SECTION("create a bool local variable and overwrite it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); }
|
||||
SECTION("create an int global variable and overwrite it") { REQUIRE(parser.process("{global myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); }
|
||||
SECTION("create a string global variable and overwrite it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); }
|
||||
SECTION("create a bool global variable and overwrite it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); }
|
||||
|
||||
SECTION("create an int local variable and redefine it") { REQUIRE(parser.process("{local myint = 33+2}{local myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); }
|
||||
SECTION("create a string local variable and redefine it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{local mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); }
|
||||
SECTION("create a bool local variable and redefine it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{local mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); }
|
||||
SECTION("create an int global variable and redefine it") { REQUIRE(parser.process("{global myint = 33+2}{global myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); }
|
||||
SECTION("create a string global variable and redefine it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{global mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); }
|
||||
SECTION("create a bool global variable and redefine it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{global mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); }
|
||||
|
||||
SECTION("create an ints local variable with array()") { REQUIRE(parser.process("{local myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); }
|
||||
SECTION("create a strings local variable array()") { REQUIRE(parser.process("{local mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
|
||||
SECTION("create a bools local variable array()") { REQUIRE(parser.process("{local mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); }
|
||||
SECTION("create an ints global variable array()") { REQUIRE(parser.process("{global myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
|
||||
SECTION("create a strings global variable array()") { REQUIRE(parser.process("{global mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
|
||||
SECTION("create a bools global variable array()") { REQUIRE(parser.process("{global mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
|
||||
|
||||
SECTION("create an ints local variable with initializer list") { REQUIRE(parser.process("{local myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, nullptr) == "24"); }
|
||||
SECTION("create a strings local variable with initializer list") { REQUIRE(parser.process("{local mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
|
||||
SECTION("create a bools local variable with initializer list") { REQUIRE(parser.process("{local mybool = (3*3 == 8, 1 + 1 == 2)}{mybool[1]}", 0, nullptr, nullptr, nullptr) == "true"); }
|
||||
SECTION("create an ints global variable with initializer list") { REQUIRE(parser.process("{global myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
|
||||
SECTION("create a strings global variable with initializer list") { REQUIRE(parser.process("{global mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
|
||||
SECTION("create a bools global variable with initializer list") { REQUIRE(parser.process("{global mybool = (2*3 == 8, 1 + 1 == 2, 5*5 != 33)}{mybool[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
|
||||
|
||||
SECTION("create an ints local variable by a copy") { REQUIRE(parser.process("{local myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357"); }
|
||||
SECTION("create a strings local variable by a copy") { REQUIRE(parser.process("{local mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes"); }
|
||||
SECTION("create a bools local variable by a copy") { REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); }
|
||||
SECTION("create an ints global variable by a copy") { REQUIRE(parser.process("{global myint = temperature}{myint[0]}", 0, &config, nullptr, &context_with_global_dict) == "357"); }
|
||||
SECTION("create a strings global variable by a copy") { REQUIRE(parser.process("{global mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, &context_with_global_dict) == "testnotes"); }
|
||||
SECTION("create a bools global variable by a copy") { REQUIRE(parser.process("{global mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, &context_with_global_dict) == "true"); }
|
||||
|
||||
SECTION("create an ints local variable by a copy and overwrite it") {
|
||||
REQUIRE(parser.process("{local myint = temperature}{myint = array(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24");
|
||||
REQUIRE(parser.process("{local myint = temperature}{myint = (2*3, 4*6)}{myint[1]}", 0, &config, nullptr, nullptr) == "24");
|
||||
REQUIRE(parser.process("{local myint = temperature}{myint = (1)}{myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357");
|
||||
}
|
||||
SECTION("create a strings local variable by a copy and overwrite it") {
|
||||
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine");
|
||||
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr[1]}", 0, &config, nullptr, nullptr) == "mineonlymine");
|
||||
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes");
|
||||
}
|
||||
SECTION("create a bools local variable by a copy and overwrite it") {
|
||||
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = array(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true");
|
||||
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, true)}{mybool[1]}", 0, &config, nullptr, nullptr) == "true");
|
||||
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, false)}{mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true");
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user