Merge branch 'master' into fs_svg

This commit is contained in:
Filip Sykala - NTB T15p 2023-04-06 16:09:19 +02:00
commit 92115754c4
6 changed files with 161 additions and 45 deletions

View File

@ -1,3 +1,5 @@
min_slic3r_version = 2.6.0-alpha6
1.0.2 Updated g-code flavor and travel accelerations.
min_slic3r_version = 2.4.2 min_slic3r_version = 2.4.2
1.0.1 Added 350mm Voron v1 variant. Updated max print heights. Removed redundant v1 volcano nozzle variants. 1.0.1 Added 350mm Voron v1 variant. Updated max print heights. Removed redundant v1 volcano nozzle variants.
min_slic3r_version = 2.4.0-beta0 min_slic3r_version = 2.4.0-beta0

View File

@ -7,7 +7,7 @@
name = Voron name = Voron
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.0.1 config_version = 1.0.2
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Voron/ config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Voron/
@ -183,7 +183,8 @@ deretract_speed = 25
end_gcode = print_end ;end script from macro end_gcode = print_end ;end script from macro
extruder_colour = #FFE3CA extruder_colour = #FFE3CA
extruder_offset = 0x0 extruder_offset = 0x0
gcode_flavor = marlin gcode_flavor = klipper
autoemit_temperature_commands = 1
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
machine_max_acceleration_e = 10000 machine_max_acceleration_e = 10000
machine_max_acceleration_extruding = 1500 machine_max_acceleration_extruding = 1500
@ -672,7 +673,8 @@ brim_width = 0
clip_multipart_objects = 1 clip_multipart_objects = 1
compatible_printers = compatible_printers =
complete_objects = 0 complete_objects = 0
default_acceleration = 3000 default_acceleration = 2000
travel_acceleration = 3000
dont_support_bridges = 1 dont_support_bridges = 1
ensure_vertical_shell_thickness = 1 ensure_vertical_shell_thickness = 1
external_perimeters_first = 0 external_perimeters_first = 0

View File

@ -1,6 +1,7 @@
#include "PlaceholderParser.hpp" #include "PlaceholderParser.hpp"
#include "Exception.hpp" #include "Exception.hpp"
#include "Flow.hpp" #include "Flow.hpp"
#include "Utils.hpp"
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <iomanip> #include <iomanip>
@ -204,6 +205,7 @@ namespace client
explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_DOUBLE), it_range(it_begin, it_end) { m_data.d = d; } explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_DOUBLE), it_range(it_begin, it_end) { m_data.d = d; }
explicit expr(const char *s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } explicit expr(const char *s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
explicit expr(std::string &&s) : m_type(TYPE_STRING) { m_data.s = new std::string(std::move(s)); }
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) : 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); } m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); }
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end } explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end }
@ -904,9 +906,12 @@ namespace client
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
if (opt == nullptr) { if (opt == nullptr) {
// Check whether the opt_key ends with '_'. // Check whether the opt_key ends with '_'.
if (opt_key_str.back() == '_') if (opt_key_str.back() == '_') {
opt_key_str.resize(opt_key_str.size() - 1); opt_key_str.resize(opt_key_str.size() - 1);
opt = ctx->resolve_symbol(opt_key_str); opt = ctx->resolve_symbol(opt_key_str);
}
if (opt == nullptr)
ctx->throw_exception("Variable does not exist", opt_key);
} }
if (! opt->is_vector()) if (! opt->is_vector())
ctx->throw_exception("Trying to index a scalar variable", opt_key); ctx->throw_exception("Trying to index a scalar variable", opt_key);
@ -1701,7 +1706,7 @@ namespace client
// This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character. // This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character.
// If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown. // If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown.
struct utf8_char_skipper_parser : qi::primitive_parser<utf8_char_skipper_parser> struct utf8_char_parser : qi::primitive_parser<utf8_char_parser>
{ {
// Define the attribute type exposed by this parser component // Define the attribute type exposed by this parser component
template <typename Context, typename Iterator> template <typename Context, typename Iterator>
@ -1710,9 +1715,10 @@ namespace client
typedef wchar_t type; typedef wchar_t type;
}; };
// This function is called during the actual parsing process // This function is called during the actual parsing process to skip whitespaces.
// Also it throws if it encounters valid or invalid UTF-8 sequence.
template <typename Iterator, typename Context , typename Skipper, typename Attribute> template <typename Iterator, typename Context , typename Skipper, typename Attribute>
bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr) const bool parse(Iterator &first, Iterator const &last, Context &context, Skipper const &skipper, Attribute& attr) const
{ {
// The skipper shall always be empty, any white space will be accepted. // The skipper shall always be empty, any white space will be accepted.
// skip_over(first, last, skipper); // skip_over(first, last, skipper);
@ -1762,6 +1768,38 @@ namespace client
} }
}; };
// This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character.
// If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown.
struct ascii_char_skipper_parser : public utf8_char_parser
{
// This function is called during the actual parsing process
template <typename Iterator, typename Context, typename Skipper, typename Attribute>
bool parse(Iterator &first, Iterator const &last, Context &context, Skipper const &skipper, Attribute &attr) const
{
Iterator it = first;
// Let the UTF-8 parser throw if it encounters an invalid UTF-8 sequence.
if (! utf8_char_parser::parse(it, last, context, skipper, attr))
return false;
char c = *first;
if (it - first > 1 || c < 0)
MyContext::throw_exception("Non-ASCII7 characters are only allowed inside text blocks and string literals, not inside code blocks.", IteratorRange(first, it));
if (c == '\r' || c == '\n' || c == '\t' || c == ' ') {
// Skip the whitespaces
++ first;
return true;
} else
// Stop skipping, let this 7bit ASCII character be processed.
return false;
}
// This function is called during error handling to create a human readable string for the error context.
template <typename Context>
spirit::info what(Context&) const
{
return spirit::info("ASCII7_char");
}
};
struct FactorActions { struct FactorActions {
static void set_start_pos(Iterator &start_pos, expr &out) static void set_start_pos(Iterator &start_pos, expr &out)
{ out.it_range = IteratorRange(start_pos, start_pos); } { out.it_range = IteratorRange(start_pos, start_pos); }
@ -1790,8 +1828,49 @@ namespace client
if (ctx->skipping()) { if (ctx->skipping()) {
out.reset(); out.reset();
out.it_range = it_range; out.it_range = it_range;
} else } else {
out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); // Unescape the string, UTF-8 safe.
std::string s;
auto begin = std::next(it_range.begin());
auto end = std::prev(it_range.end());
assert(begin <= end);
{
// 1) Get the size of the string after unescaping.
size_t len = 0;
for (auto it = begin; it != end;) {
if (*it == '\\') {
if (++ it == end ||
(*it != 'r' && *it != 'n' && *it != '"' && *it != '\\'))
ctx->throw_exception("Invalid escape sequence", {std::prev(it), std::next(it) });
++ len;
++ it;
} else {
size_t n = get_utf8_sequence_length(&*it, end - it);
len += n;
it += n;
}
}
// and reserve the string.
s.reserve(len);
}
// 2) Copy & unescape the string.
for (auto it = begin; it != end;) {
if (*it == '\\') {
char c = *(++ it);
if (c == 'r')
c = '\r';
else if (c == 'n')
c = '\n';
s += c;
++ it;
} else {
size_t n = get_utf8_sequence_length(&*it, end - it);
s.append(&*it, n);
it += n;
}
}
out = expr(std::move(s), it_range.begin(), it_range.end());
}
} }
static void expr_(expr &value, Iterator &end_pos, expr &out) static void expr_(expr &value, Iterator &end_pos, expr &out)
{ auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); }
@ -1807,11 +1886,13 @@ namespace client
static void noexpr(expr &out) { out.reset(); } static void noexpr(expr &out) { out.reset(); }
}; };
using skipper = ascii_char_skipper_parser;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Our macro_processor grammar // Our macro_processor grammar
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html // Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper>
{ {
macro_processor() : macro_processor::base_type(start) macro_processor() : macro_processor::base_type(start)
{ {
@ -1825,7 +1906,7 @@ namespace client
qi::no_skip_type no_skip; qi::no_skip_type no_skip;
qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double; qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double;
spirit_encoding::char_type char_; spirit_encoding::char_type char_;
utf8_char_skipper_parser utf8char; utf8_char_parser utf8char;
spirit::bool_type bool_; spirit::bool_type bool_;
spirit::int_type int_; spirit::int_type int_;
spirit::double_type double_; spirit::double_type double_;
@ -2165,22 +2246,22 @@ namespace client
} }
// Generic expression over expr. // Generic expression over expr.
typedef qi::rule<Iterator, expr(const MyContext*), spirit_encoding::space_type> RuleExpression; typedef qi::rule<Iterator, expr(const MyContext*), skipper> RuleExpression;
// The start of the grammar. // The start of the grammar.
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> start; qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper> start;
// A free-form text. // A free-form text.
qi::rule<Iterator, std::string(), spirit_encoding::space_type> text; qi::rule<Iterator, std::string(), skipper> text;
// A free-form text, possibly empty, possibly containing macro expansions. // A free-form text, possibly empty, possibly containing macro expansions.
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block; qi::rule<Iterator, std::string(const MyContext*), skipper> text_block;
// Statements enclosed in curely braces {} // Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> block, statement, macros, if_text_block, if_macros, else_macros; qi::rule<Iterator, std::string(const MyContext*), skipper> block, statement, macros, if_text_block, if_macros, else_macros;
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. // 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_encoding::space_type> legacy_variable_expansion; qi::rule<Iterator, std::string(const MyContext*), skipper> legacy_variable_expansion;
// Parsed identifier name. // Parsed identifier name.
qi::rule<Iterator, IteratorRange(), spirit_encoding::space_type> identifier; qi::rule<Iterator, IteratorRange(), skipper> identifier;
// Ternary operator (?:) over logical_or_expression. // Ternary operator (?:) over logical_or_expression.
qi::rule<Iterator, expr(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> conditional_expression; qi::rule<Iterator, expr(const MyContext*), qi::locals<bool>, skipper> conditional_expression;
// Logical or over logical_and_expressions. // Logical or over logical_and_expressions.
RuleExpression logical_or_expression; RuleExpression logical_or_expression;
// Logical and over relational_expressions. // Logical and over relational_expressions.
@ -2198,27 +2279,27 @@ namespace client
// Accepting an optional parameter. // Accepting an optional parameter.
RuleExpression optional_parameter; RuleExpression optional_parameter;
// Rule to capture a regular expression enclosed in //. // Rule to capture a regular expression enclosed in //.
qi::rule<Iterator, IteratorRange(), spirit_encoding::space_type> regular_expression; qi::rule<Iterator, IteratorRange(), skipper> regular_expression;
// Evaluate boolean expression into bool. // Evaluate boolean expression into bool.
qi::rule<Iterator, bool(const MyContext*), spirit_encoding::space_type> bool_expr_eval; qi::rule<Iterator, bool(const MyContext*), skipper> bool_expr_eval;
// Reference of a scalar variable, or reference to a field of a vector variable. // Reference of a scalar variable, or reference to a field of a vector variable.
qi::rule<Iterator, OptWithPos(const MyContext*), qi::locals<OptWithPos, int>, spirit_encoding::space_type> variable_reference; qi::rule<Iterator, OptWithPos(const MyContext*), qi::locals<OptWithPos, int>, skipper> variable_reference;
// Rule to translate an identifier to a ConfigOption, or to fail. // Rule to translate an identifier to a ConfigOption, or to fail.
qi::rule<Iterator, OptWithPos(const MyContext*), spirit_encoding::space_type> variable; qi::rule<Iterator, OptWithPos(const MyContext*), skipper> variable;
// Evaluating whether a nullable variable is nil. // Evaluating whether a nullable variable is nil.
qi::rule<Iterator, expr(const MyContext*), spirit_encoding::space_type> is_nil_test; qi::rule<Iterator, expr(const MyContext*), skipper> is_nil_test;
// Evaluating "one of" list of patterns. // Evaluating "one of" list of patterns.
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, spirit_encoding::space_type> one_of; qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, skipper> one_of;
qi::rule<Iterator, expr(const MyContext*, const expr &param), spirit_encoding::space_type> one_of_list; qi::rule<Iterator, expr(const MyContext*, const expr &param), skipper> one_of_list;
// Evaluating the "interpolate_table" expression. // Evaluating the "interpolate_table" expression.
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, spirit_encoding::space_type> interpolate_table; qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, skipper> interpolate_table;
qi::rule<Iterator, InterpolateTableContext(const MyContext*, const expr &param), spirit_encoding::space_type> interpolate_table_list; qi::rule<Iterator, InterpolateTableContext(const MyContext*, const expr &param), skipper> interpolate_table_list;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> if_else_output; qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper> if_else_output;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos>, spirit_encoding::space_type> assignment_statement; qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos>, skipper> assignment_statement;
// Allocating new local or global variables. // Allocating new local or global variables.
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable>, spirit_encoding::space_type> new_variable_statement; qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable>, skipper> new_variable_statement;
qi::rule<Iterator, std::vector<expr>(const MyContext*), spirit_encoding::space_type> initializer_list; qi::rule<Iterator, std::vector<expr>(const MyContext*), skipper> initializer_list;
qi::symbols<char> keywords; qi::symbols<char> keywords;
}; };
@ -2229,7 +2310,7 @@ static const client::macro_processor g_macro_processor_instance;
static std::string process_macro(const std::string &templ, client::MyContext &context) static std::string process_macro(const std::string &templ, client::MyContext &context)
{ {
std::string output; std::string output;
phrase_parse(templ.begin(), templ.end(), g_macro_processor_instance(&context), spirit_encoding::space_type{}, output); phrase_parse(templ.begin(), templ.end(), g_macro_processor_instance(&context), client::skipper{}, output);
if (! context.error_message.empty()) { if (! context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r') if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n'; context.error_message += '\n';

View File

@ -1509,7 +1509,9 @@ void PrintObject::discover_vertical_shells()
Polygons internal_volume; Polygons internal_volume;
{ {
Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{}; Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{};
Polygons shrinked_upper_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer + 1]->lslices) : Polygons{}; Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ?
to_polygons(m_layers[idx_layer + 1]->lslices) :
Polygons{};
internal_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); internal_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice);
} }
@ -1739,7 +1741,7 @@ void PrintObject::bridge_over_infill()
// cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another
std::vector<std::vector<size_t>> clustered_layers_for_threads; std::vector<std::vector<size_t>> clustered_layers_for_threads;
float target_flow_height_factor = 0.75; float target_flow_height_factor = 0.9;
{ {
std::vector<size_t> layers_with_candidates; std::vector<size_t> layers_with_candidates;
std::map<size_t, Polygons> layer_area_covered_by_candidates; std::map<size_t, Polygons> layer_area_covered_by_candidates;
@ -1768,7 +1770,7 @@ void PrintObject::bridge_over_infill()
if (clustered_layers_for_threads.empty() || if (clustered_layers_for_threads.empty() ||
this->get_layer(clustered_layers_for_threads.back().back())->print_z < this->get_layer(clustered_layers_for_threads.back().back())->print_z <
this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->print_z -
this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() * target_flow_height_factor - this->get_layer(pair.first)->regions()[0]->bridging_flow(frSolidInfill, true).height() * target_flow_height_factor -
EPSILON || EPSILON ||
intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()], intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()],
layer_area_covered_by_candidates[pair.first]) layer_area_covered_by_candidates[pair.first])
@ -1798,9 +1800,9 @@ void PrintObject::bridge_over_infill()
ExPolygons not_sparse_infill{}; ExPolygons not_sparse_infill{};
double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON;
for (int i = int(lidx) - 1; i >= 0; --i) { for (int i = int(lidx) - 1; i >= 0; --i) {
// Stop iterating if layer is lower than bottom_z. // Stop iterating if layer is lower than bottom_z and at least one iteration was made
const Layer *layer = po->get_layer(i); const Layer *layer = po->get_layer(i);
if (layer->print_z < bottom_z) if (layer->print_z < bottom_z && i < int(lidx) - 1)
break; break;
for (const LayerRegion *region : layer->regions()) { for (const LayerRegion *region : layer->regions()) {
@ -2103,9 +2105,10 @@ void PrintObject::bridge_over_infill()
} }
// Gather deep infill areas, where thick bridges fit // Gather deep infill areas, where thick bridges fit
coordf_t spacing = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).scaled_spacing(); coordf_t spacing = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).scaled_spacing();
coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor; coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).height() *
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height); target_flow_height_factor;
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height);
{ {
// Now also remove area that has been already filled on lower layers by bridging expansion - For this // Now also remove area that has been already filled on lower layers by bridging expansion - For this
@ -2144,20 +2147,27 @@ void PrintObject::bridge_over_infill()
expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON);
expansion_area = intersection(expansion_area, deep_infill_area); expansion_area = intersection(expansion_area, deep_infill_area);
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5);
#ifdef DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area", debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area",
to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors)); to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors));
#endif #endif
std::vector<CandidateSurface> expanded_surfaces; std::vector<CandidateSurface> expanded_surfaces;
expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
area_to_be_bridge.erase(std::remove_if(area_to_be_bridge.begin(), area_to_be_bridge.end(),
[internal_unsupported_area](const Polygon &p) {
return intersection({p}, internal_unsupported_area).empty();
}),
area_to_be_bridge.end());
Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
if (area_to_be_bridge.empty()) if (area_to_be_bridge.empty())
continue; continue;

View File

@ -529,7 +529,7 @@ SCENARIO("Perimeters3", "[Perimeters]")
auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ auto config = Slic3r::DynamicPrintConfig::full_print_config_with({
{ "skirts", 0 }, { "skirts", 0 },
{ "perimeters", 3 }, { "perimeters", 3 },
{ "layer_height", 0.4 }, { "layer_height", 0.15 },
{ "bridge_speed", 99 }, { "bridge_speed", 99 },
{ "enable_dynamic_overhang_speeds", false }, { "enable_dynamic_overhang_speeds", false },
// to prevent bridging over sparse infill // to prevent bridging over sparse infill

View File

@ -44,6 +44,27 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("multiple expressions with semicolons 2") { 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"); } SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); }
SECTION("parsing string with escaped characters") { REQUIRE(parser.process("{\"hu\\nha\\\\\\\"ha\\\"\"}") == "hu\nha\\\"ha\""); }
WHEN("An UTF-8 character is used inside the code block") {
THEN("A std::runtime_error exception is thrown.") {
// full-width plus sign instead of plain +
REQUIRE_THROWS_AS(parser.process("{1\xEF\xBC\x8B 3}"), std::runtime_error);
}
}
WHEN("An UTF-8 character is used inside a string") {
THEN("UTF-8 sequence is processed correctly when quoted") {
// japanese "cool" or "stylish"
REQUIRE(parser.process("{1+\"\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84\"+\" \"+3}") == "1\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84 3");
}
}
WHEN("An UTF-8 character is used inside a string") {
THEN("UTF-8 sequence is processed correctly outside of code blocks") {
// japanese "cool" or "stylish"
REQUIRE(parser.process("{1+3}\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84") == "4\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84");
}
}
// Test the math expressions. // Test the math expressions.
SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); } SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); }
SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); } SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); }