diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 7c2a75f29..4c1c1b108 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 77; +use Test::More tests => 81; use strict; use warnings; @@ -71,6 +71,10 @@ use Slic3r::Test; is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)'; is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)'; ok abs($parser->process('{2.5*bar*(3-12)}') - -45) < 1e-7, 'math: 2.5*bar*(3-12)'; + is $parser->process('{min(12, 14)}'), '12', 'math: min(12, 14)'; + is $parser->process('{max(12, 14)}'), '14', 'math: max(12, 14)'; + is $parser->process('{min(13.4, -1238.1)}'), '-1238.1', 'math: min(13.4, -1238.1)'; + is $parser->process('{max(13.4, -1238.1)}'), '13.4', 'math: max(13.4, -1238.1)'; # Test the boolean expression parser. is $parser->evaluate_boolean_expression('12 == 12'), 1, 'boolean expression parser: 12 == 12'; diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index dc8e5d691..5e5b74efc 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -789,14 +789,19 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } // Process filament-specific gcode in extruder order. - if (print.config.single_extruder_multi_material) { - // Process the end_filament_gcode for the active filament only. - _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id())); - } else { - for (const std::string &end_gcode : print.config.end_filament_gcode.values) - _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); + { + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z - m_config.z_offset.value)); + if (print.config.single_extruder_multi_material) { + // Process the end_filament_gcode for the active filament only. + _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config)); + } else { + for (const std::string &end_gcode : print.config.end_filament_gcode.values) + _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front(), &config))); + } + _writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id(), &config)); } - _writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id())); _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% _write(file, m_writer.postamble()); diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 62b516935..80740b20d 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -414,6 +414,7 @@ namespace client lhs.type = TYPE_BOOL; lhs.data.b = invert ? ! value : value; } + // Compare operators, store the result into lhs. static void equal (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '=', false); } static void not_equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '=', true ); } static void lower (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', false); } @@ -421,6 +422,40 @@ namespace client static void leq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '>', true ); } static void geq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', true ); } + enum Function2ParamsType { + FUNCTION_MIN, + FUNCTION_MAX, + }; + // Store the result into param1. + static void function_2params(expr ¶m1, expr ¶m2, Function2ParamsType fun) + { + const char *err_msg = "Not a numeric type."; + param1.throw_if_not_numeric(err_msg); + param2.throw_if_not_numeric(err_msg); + if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) { + double d = 0.; + switch (fun) { + case FUNCTION_MIN: d = std::min(param1.as_d(), param2.as_d()); break; + case FUNCTION_MAX: d = std::max(param1.as_d(), param2.as_d()); break; + default: param1.throw_exception("Internal error: invalid function"); + } + param1.data.d = d; + param1.type = TYPE_DOUBLE; + } else { + int i = 0.; + switch (fun) { + case FUNCTION_MIN: i = std::min(param1.as_i(), param2.as_i()); break; + case FUNCTION_MAX: i = std::max(param1.as_i(), param2.as_i()); break; + default: param1.throw_exception("Internal error: invalid function"); + } + param1.data.i = i; + param1.type = TYPE_INT; + } + } + // Store the result into param1. + static void min(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MIN); } + static void max(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MAX); } + static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) { const std::string *subject = nullptr; @@ -1019,6 +1054,10 @@ namespace client | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] | ((kw["not"] | '!') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] + | (kw["min"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') + [ px::bind(&expr::min, _val, _2) ] + | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') + [ px::bind(&expr::max, _val, _2) ] | (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) ] @@ -1051,6 +1090,8 @@ namespace client ("elsif") ("endif") ("false") + ("min") + ("max") ("not") ("or") ("true");