refactoring overhang speed

This commit is contained in:
Pavel Mikus 2022-12-11 23:04:40 +01:00 committed by Pavel Mikuš
parent 3714943b49
commit f730fd21d4
12 changed files with 126 additions and 70 deletions

View File

@ -851,7 +851,6 @@ namespace DoExport {
if (region.config().get_abs_value("perimeter_speed") == 0 ||
region.config().get_abs_value("small_perimeter_speed") == 0 ||
region.config().get_abs_value("external_perimeter_speed") == 0 ||
region.config().get_abs_value("overhang_speed") == 0 ||
region.config().get_abs_value("bridge_speed") == 0)
mm3_per_mm.push_back(layerm->perimeters().min_mm3_per_mm());
if (region.config().get_abs_value("infill_speed") == 0 ||
@ -2877,9 +2876,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
speed = m_config.get_abs_value("perimeter_speed");
} else if (path.role() == erExternalPerimeter) {
speed = m_config.get_abs_value("external_perimeter_speed");
}else if (path.role() == erOverhangPerimeter){
speed = m_config.get_abs_value("overhang_speed");
} else if (path.role() == erBridgeInfill) {
} else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill) {
speed = m_config.get_abs_value("bridge_speed");
} else if (path.role() == erInternalInfill) {
speed = m_config.get_abs_value("infill_speed");
@ -2918,9 +2915,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
bool variable_speed = false;
std::vector<ProcessedPoint> new_points{};
if (this->m_config.overhangs && !this->on_first_layer() && is_perimeter(path.role())) {
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path);
variable_speed = std::any_of(new_points.begin(), new_points.end(), [](const ProcessedPoint &p) { return p.speed_factor != 1.0; });
if (this->m_config.enable_dynamic_overhang_speeds && !this->on_first_layer() && is_perimeter(path.role())) {
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, m_config.overhang_overlaps,
m_config.dynamic_overhang_speeds,
m_config.get_abs_value("external_perimeter_speed"), speed);
variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed; });
}
double F = speed * 60; // convert mm/sec to mm/min
@ -2996,7 +2995,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front());
auto it = path.polyline.points.begin();
auto end = path.polyline.points.end();
for (++it; it != end; ++it) {
for (++ it; it != end; ++ it) {
Vec2d p = this->point_to_gcode_quantized(*it);
const double line_length = (p - prev).norm();
path_length += line_length;
@ -3004,22 +3003,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
prev = p;
}
} else {
double overhang_speed = m_config.get_abs_value("overhang_speed");
std::string comment;
if (m_config.gcode_comments) {
comment = description;
comment += description_bridge;
}
double last_set_speed = std::max(overhang_speed, new_points[0].speed_factor * speed) * 60.0;
double last_set_speed = new_points[0].speed * 60.0;
gcode += m_writer.set_speed(last_set_speed, "", comment);
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
for (size_t i = 1; i < new_points.size(); i++) {
const ProcessedPoint& procesed_point = new_points[i];
Vec2d p = this->point_to_gcode_quantized(procesed_point.p);
const ProcessedPoint& processed_point = new_points[i];
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
const double line_length = (p - prev).norm();
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
prev = p;
double new_speed = std::max(overhang_speed, procesed_point.speed_factor * speed) * 60.0;
double new_speed = processed_point.speed * 60.0;
if (last_set_speed != new_speed) {
gcode += m_writer.set_speed(new_speed, "", comment);
last_set_speed = new_speed;

View File

@ -12,6 +12,7 @@
#include "../Polygon.hpp"
#include "../ClipperUtils.hpp"
#include "../Flow.hpp"
#include "../Config.hpp"
#include <algorithm>
#include <cmath>
@ -19,6 +20,7 @@
#include <limits>
#include <numeric>
#include <unordered_map>
#include <utility>
#include <vector>
namespace Slic3r {
@ -67,8 +69,8 @@ public:
class CurvatureEstimator
{
static const size_t sliders_count = 3;
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{2.0}, {4.0}, {8.0}};
static const size_t sliders_count = 2;
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{1.5}, {3.0}};
public:
void add_point(float distance, float angle)
@ -189,7 +191,7 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
struct ProcessedPoint
{
Point p;
float speed_factor = 1.0f;
float speed = 1.0f;
};
class ExtrusionQualityEstimator
@ -209,33 +211,53 @@ public:
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
}
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path)
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path,
const ConfigOptionPercents &overlaps,
const ConfigOptionFloatsOrPercents &speeds,
float ext_perimeter_speed,
float original_speed)
{
size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
std::vector<std::pair<float, float>> speed_sections;
for (size_t i = 0; i < speed_sections_count; i++) {
float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0));
float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
speed_sections.push_back({distance, speed});
}
std::sort(speed_sections.begin(), speed_sections.end(),
[](const std::pair<float, float> &a, const std::pair<float, float> &b) { return a.first < b.first; });
std::vector<ExtendedPoint> extended_points =
estimate_points_properties<true, true, true, false>(path.polyline.points, prev_layer_boundaries[current_object], path.width);
float min_malformation_dist = 0.55 * path.width;
float peak_malformation_dist = path.width;
std::vector<ProcessedPoint> processed_points;
processed_points.reserve(extended_points.size());
for (size_t i = 0; i < extended_points.size(); i++) {
const ExtendedPoint &curr = extended_points[i];
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
float extrusion_speed_factor = 1.0f;
if (std::max(curr.distance, next.distance) < min_malformation_dist) {
extrusion_speed_factor = 1.0f;
auto calculate_speed = [&speed_sections, &original_speed](float distance) {
float final_speed;
if (distance <= speed_sections.front().first) {
final_speed = original_speed;
} else if (distance >= speed_sections.back().first) {
final_speed = speed_sections.back().second;
} else {
float curvature_penalty = std::min(1.0f, next.curvature);
float distance_penalty = (std::max(curr.distance, next.distance) - min_malformation_dist) /
(peak_malformation_dist - min_malformation_dist);
distance_penalty = std::min(1.0f, distance_penalty);
extrusion_speed_factor = std::clamp(1.0f - distance_penalty - curvature_penalty, 0.0f, 1.0f);
size_t section_idx = 0;
while (distance > speed_sections[section_idx + 1].first) {
section_idx++;
}
float t = (distance - speed_sections[section_idx].first) /
(speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
t = std::clamp(t, 0.0f, 1.0f);
final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
}
return final_speed;
};
processed_points.push_back({scaled(curr.position), extrusion_speed_factor});
float extrusion_speed = (calculate_speed(curr.distance) + calculate_speed(next.distance)) / 2.0f;
processed_points.push_back({scaled(curr.position), extrusion_speed});
}
return processed_points;
}

View File

@ -474,7 +474,6 @@ void Layer::make_perimeters()
&& config.perimeters == other_config.perimeters
&& config.perimeter_speed == other_config.perimeter_speed
&& config.external_perimeter_speed == other_config.external_perimeter_speed
&& config.overhang_speed == other_config.overhang_speed
&& (config.gap_fill_enabled ? config.gap_fill_speed.value : 0.) ==
(other_config.gap_fill_enabled ? other_config.gap_fill_speed.value : 0.)
&& config.overhangs == other_config.overhangs

View File

@ -428,7 +428,8 @@ static std::vector<std::string> s_Preset_print_options {
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "overhang_speed", "infill_speed", "solid_infill_speed",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"enable_dynamic_overhang_speeds", "dynamic_overhang_speeds", "overhang_overlaps",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",

View File

@ -528,6 +528,36 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60));
def = this->add("enable_dynamic_overhang_speeds", coBool);
def->label = L("Enable dynamic overhang speeds (Experimental)");
def->category = L("Speed");
def->tooltip = L("This setting enables dynamic speed control on overhangs.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("overhang_overlaps", coPercents);
def->label = L("Overhang overlap percentage");
def->category = L("Speed");
def->tooltip = L("Controls percentage of overhang extrusion overlap with the previous layer."
"Each overlap size then corresponds with the overhang speed set below.");
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercents({60, 40, 20, 0}));
def = this->add("dynamic_overhang_speeds", coFloatsOrPercents);
def->label = L("Dynamic speed on overhangs");
def->category = L("Speed");
def->tooltip = L("This setting controls the speed of the overhangs for overlap values set above."
"The final speed is calculated as an interpolation of the set speed values."
"If set as percentage, the speeds are calculated over the external perimeter speed."
);
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatsOrPercents({{25, false}, {20, false}, {15, false}, {15, false}}));
def = this->add("brim_width", coFloat);
def->label = L("Brim width");
def->category = L("Skirt and brim");
@ -2358,18 +2388,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
def = this->add("overhang_speed", coFloatOrPercent);
def->label = L("Overhangs");
def->category = L("Speed");
def->tooltip = L("This setting controls the speed of overhangs."
"If expressed as percentage (for example: 80%) it will be calculated "
"on the external perimeters speed setting. Set to zero for auto.");
def->sidetext = L("mm/s or %");
def->ratio_over = "external_perimeter_speed";
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
def = this->add("solid_infill_below_area", coFloat);
def->label = L("Solid infill threshold area");
def->category = L("Infill");

View File

@ -564,7 +564,9 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<InfillPattern>, bottom_fill_pattern))
((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width))
((ConfigOptionFloatOrPercent, external_perimeter_speed))
((ConfigOptionFloatOrPercent, overhang_speed))
((ConfigOptionBool, enable_dynamic_overhang_speeds))
((ConfigOptionPercents, overhang_overlaps))
((ConfigOptionFloatsOrPercents, dynamic_overhang_speeds))
((ConfigOptionBool, external_perimeters_first))
((ConfigOptionBool, extra_perimeters))
((ConfigOptionBool, extra_perimeters_on_overhangs))

View File

@ -748,7 +748,9 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_material_speed"
|| opt_key == "support_material_interface_speed"
|| opt_key == "bridge_speed"
|| opt_key == "overhang_speed"
|| opt_key == "enable_dynamic_overhang_speeds"
|| opt_key == "overhang_overlaps"
|| opt_key == "dynamic_overhang_speeds"
|| opt_key == "external_perimeter_speed"
|| opt_key == "infill_speed"
|| opt_key == "perimeter_speed"

View File

@ -220,9 +220,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
bool have_perimeters = config->opt_int("perimeters") > 0;
for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "overhang_speed" })
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds", "overhang_overlaps", "dynamic_overhang_speeds" })
toggle_field(el, have_perimeters);
toggle_field("overhang_overlaps", config->opt_bool("enable_dynamic_overhang_speeds"));
toggle_field("dynamic_overhang_speeds", config->opt_bool("enable_dynamic_overhang_speeds"));
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
// infill_extruder uses the same logic as in Print::extruders()
for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed",

View File

@ -100,7 +100,6 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
double bridge_flow_ratio = print_config.opt_float("bridge_flow_ratio");
double perimeter_speed = print_config.opt_float("perimeter_speed");
double external_perimeter_speed = print_config.get_abs_value("external_perimeter_speed", perimeter_speed);
// double overhang_seed = print_config.get_abs_value("overhang_speed", external_perimeter_speed);
// double gap_fill_speed = print_config.opt_bool("gap_fill_enabled") ? print_config.opt_float("gap_fill_speed") : 0.;
double infill_speed = print_config.opt_float("infill_speed");
double small_perimeter_speed = print_config.get_abs_value("small_perimeter_speed", perimeter_speed);

View File

@ -1,4 +1,5 @@
// #include "libslic3r/GCodeSender.hpp"
#include "slic3r/GUI/BedShapeDialog.hpp"
#include "slic3r/Utils/Serial.hpp"
#include "Tab.hpp"
#include "PresetHints.hpp"
@ -1541,7 +1542,6 @@ void TabPrint::build()
optgroup->append_single_option_line("perimeter_speed");
optgroup->append_single_option_line("small_perimeter_speed");
optgroup->append_single_option_line("external_perimeter_speed");
optgroup->append_single_option_line("overhang_speed");
optgroup->append_single_option_line("infill_speed");
optgroup->append_single_option_line("solid_infill_speed");
optgroup->append_single_option_line("top_solid_infill_speed");
@ -1551,6 +1551,20 @@ void TabPrint::build()
optgroup->append_single_option_line("gap_fill_speed");
optgroup->append_single_option_line("ironing_speed");
optgroup = page->new_optgroup(L("Dynamic overhang speed"));
auto append_option_line = [](ConfigOptionsGroupShp optgroup, std::string opt_key) {
auto option = optgroup->get_option(opt_key, 0);
auto line = Line{option.opt.full_label, ""};
line.append_option(option);
line.append_option(optgroup->get_option(opt_key, 1));
line.append_option(optgroup->get_option(opt_key, 2));
line.append_option(optgroup->get_option(opt_key, 3));
optgroup->append_line(line);
};
optgroup->append_single_option_line("enable_dynamic_overhang_speeds");
append_option_line(optgroup,"overhang_overlaps");
append_option_line(optgroup,"dynamic_overhang_speeds");
optgroup = page->new_optgroup(L("Speed for non-print moves"));
optgroup->append_single_option_line("travel_speed");
optgroup->append_single_option_line("travel_speed_z");

View File

@ -176,7 +176,7 @@ SCENARIO("Cooling integration tests", "[Cooling]") {
{ "fan_below_layer_time", { 0 } },
{ "slowdown_below_layer_time", { 0 } },
{ "bridge_speed", 99 },
{ "overhangs", false },
{ "enable_dynamic_overhang_speeds", false },
// internal bridges use solid_infil speed
{ "bottom_solid_layers", 1 },
// internal bridges use solid_infil speed

View File

@ -309,7 +309,7 @@ SCENARIO("Perimeters", "[Perimeters]")
{ "perimeters", 1 },
{ "perimeter_speed", 77 },
{ "external_perimeter_speed", 66 },
{ "overhang_speed", 60 },
{ "enable_dynamic_overhang_speeds", false },
{ "bridge_speed", 99 },
{ "cooling", "1" },
{ "fan_below_layer_time", "0" },
@ -330,11 +330,10 @@ SCENARIO("Perimeters", "[Perimeters]")
const double perimeter_speed = config.opt_float("perimeter_speed") * 60.;
const double external_perimeter_speed = config.get_abs_value("external_perimeter_speed") * 60.;
const double bridge_speed = config.opt_float("bridge_speed") * 60.;
const double overhang_speed = config.opt<ConfigOptionFloatOrPercent>("overhang_speed")->value * 60.;
const double nozzle_dmr = config.opt<ConfigOptionFloats>("nozzle_diameter")->get_at(0);
const double filament_dmr = config.opt<ConfigOptionFloats>("filament_diameter")->get_at(0);
const double bridge_mm_per_mm = sqr(nozzle_dmr / filament_dmr) * config.opt_float("bridge_flow_ratio");
parser.parse_buffer(gcode, [&layer_speeds, &fan_speed, perimeter_speed, external_perimeter_speed, bridge_speed, overhang_speed, nozzle_dmr, filament_dmr, bridge_mm_per_mm]
parser.parse_buffer(gcode, [&layer_speeds, &fan_speed, perimeter_speed, external_perimeter_speed, bridge_speed, nozzle_dmr, filament_dmr, bridge_mm_per_mm]
(Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
{
if (line.cmd_is("M107"))
@ -343,14 +342,13 @@ SCENARIO("Perimeters", "[Perimeters]")
line.has_value('S', fan_speed);
else if (line.extruding(self) && line.dist_XY(self) > 0) {
double feedrate = line.new_F(self);
REQUIRE((is_approx(feedrate, perimeter_speed) || is_approx(feedrate, bridge_speed) ||
(feedrate >= overhang_speed && feedrate <= external_perimeter_speed)));
REQUIRE((is_approx(feedrate, perimeter_speed) || is_approx(feedrate, external_perimeter_speed) || is_approx(feedrate, bridge_speed)));
layer_speeds[self.z()].insert(feedrate);
bool bridging = is_approx(feedrate, bridge_speed);
double mm_per_mm = line.dist_E(self) / line.dist_XY(self);
// Fan enabled at full speed when bridging, disabled when not bridging.
REQUIRE((! bridging || fan_speed == 255));
// REQUIRE((bridging || fan_speed == 0)); not true. with dynamic overhangs slowdown, the fan is used as well (and the speed is anywhere between external perimeter speed and overhang speed)
REQUIRE((bridging || fan_speed == 0));
// When bridging, bridge flow is applied.
REQUIRE((! bridging || std::abs(mm_per_mm - bridge_mm_per_mm) <= 0.01));
}
@ -377,7 +375,6 @@ SCENARIO("Perimeters", "[Perimeters]")
{ "external_perimeter_speed", 99 },
{ "small_perimeter_speed", 99 },
{ "thin_walls", 0 },
{ "overhangs", false },
});
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::ipadstand }, config);
@ -533,7 +530,7 @@ SCENARIO("Perimeters3", "[Perimeters]")
{ "skirts", 0 },
{ "perimeters", 3 },
{ "layer_height", 0.4 },
{ "overhang_speed", 5 },
{ "enable_dynamic_overhang_speeds", false },
// to prevent bridging over sparse infill
{ "fill_density", 0 },
{ "overhangs", true },
@ -547,25 +544,26 @@ SCENARIO("Perimeters3", "[Perimeters]")
std::string gcode = Slic3r::Test::slice({ mesh(Slic3r::Test::TestMesh::V, Vec3d::Zero(), scale) }, config);
GCodeReader parser;
std::set<coord_t> z_with_bridges;
const double overhang_speed = config.opt<ConfigOptionFloatOrPercent>("overhang_speed")->value * 60.;
parser.parse_buffer(gcode, [&z_with_bridges, overhang_speed](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
const double bridge_speed = config.opt_float("bridge_speed") * 60.;
parser.parse_buffer(gcode, [&z_with_bridges, bridge_speed](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
{
if (line.extruding(self) && line.dist_XY(self) > 0 &&
(is_approx<double>(line.new_F(self), overhang_speed)))
if (line.extruding(self) && line.dist_XY(self) > 0 && is_approx<double>(line.new_F(self), bridge_speed))
z_with_bridges.insert(scaled<coord_t>(self.z()));
});
return z_with_bridges.size();
};
GIVEN("V shape, scaled 0.5x in X") {
int n = test(Vec3d(0.5, 1., 1.));
THEN("no overhangs printed with overhangs speed") {
REQUIRE(n == 0);
GIVEN("V shape, unscaled") {
int n = test(Vec3d(1., 1., 1.));
// except for the two internal solid layers above void
THEN("no overhangs printed with bridge speed") {
REQUIRE(n == 1);
}
}
GIVEN("V shape, scaled 3x in X") {
int n = test(Vec3d(3., 1., 1.));
THEN("overhangs printed with overhangs speed") {
// except for the two internal solid layers above void
THEN("overhangs printed with bridge speed") {
REQUIRE(n > 2);
}
}