Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude speed ranges (beta release) #4381

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ffb2094
POC#1: Check & modify speed against user config input
TheSlashEffect Jul 12, 2024
a8fd3c5
POC#2: Exclude a single range, set by the user
TheSlashEffect Jul 13, 2024
d9bafcc
Add option to adjust speed to lowest or highest allowed value
TheSlashEffect Jul 14, 2024
3613890
Apply forbidden speed range filter on overhangs
TheSlashEffect Jul 14, 2024
581200a
Add TODO for broken small perimeter speed feature
TheSlashEffect Jul 15, 2024
7663bb7
Allow for multiple forbidden speed ranges.
TheSlashEffect Jul 16, 2024
82d2b05
Remove redundant regex check, refactor loop
TheSlashEffect Jul 16, 2024
d846cd8
Remove printout
TheSlashEffect Jul 16, 2024
6d1386c
Remove dev printouts
TheSlashEffect Jul 16, 2024
b0a4d71
Add early exit strategy
TheSlashEffect Jul 16, 2024
858c8ed
Refactoring: move exclude print speed logic to its own class
TheSlashEffect Jul 16, 2024
9cb8c42
Add exception handling to exclude print speed range feature
TheSlashEffect Jul 16, 2024
952e0ea
Increase UI label and input field width
TheSlashEffect Jul 16, 2024
2a432bc
Give option to adjust speed to lowest, highest, or nearest value in e…
TheSlashEffect Jul 17, 2024
aa4ea57
Constructor refactoring: extract functions, clean up code
TheSlashEffect Jul 18, 2024
a58ed74
Update gui message
TheSlashEffect Jul 18, 2024
27ff6c5
Move gui info from field to group name
TheSlashEffect Jul 18, 2024
82a5395
Update user message
TheSlashEffect Jul 18, 2024
c2d6f23
Add newline
TheSlashEffect Jul 18, 2024
b851ae5
Set feature display flags to expert
TheSlashEffect Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions resources/ui_layout/default/print.ui
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ group:label_width$8:sidetext_width$7:Speed for print moves
line:Wipe tower
setting:width$4:label$Main speed:wipe_tower_speed
setting:width$4:label$Wipe starting speed:wipe_tower_wipe_starting_speed
group:Exclude print speeds (beta)
line:Exclude print speeds
setting:label_width$12:width$26:exclude_print_speed_ranges
setting:exclude_print_speed_adjustment_direction
group:Speed for non-print moves
line:Travel speed
setting:label$xy:travel_speed
Expand Down
4 changes: 4 additions & 0 deletions resources/ui_layout/example/print.ui
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ group:Speed for non-print moves
setting:label$xy:travel_speed
setting:label$z:travel_speed_z
end_line
group:Exclude print speeds (beta)
line:Exclude print speeds
setting:label_width$12:width$26:exclude_print_speed_ranges
setting:exclude_print_speed_adjustment_direction
group:sidetext_width$7:Modifiers
line:First layer speed
setting:label_width$8:width$4:first_layer_min_speed
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ add_library(libslic3r STATIC
GCode/GCodeProcessor.hpp
GCode/AvoidCrossingPerimeters.cpp
GCode/AvoidCrossingPerimeters.hpp
GCode/ExcludePrintSpeeds.cpp
GCode/ExcludePrintSpeeds.hpp
GCode.cpp
GCode.hpp
GCodeReader.cpp
Expand Down
21 changes: 19 additions & 2 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,16 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene
m_pressure_equalizer = make_unique<PressureEqualizer>(print.config());
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;

try {
if (!print.config().exclude_print_speed_ranges.empty()) {
m_exclude_print_speeds =
make_unique<ExcludePrintSpeeds>(print.config().exclude_print_speed_ranges,
print.config().exclude_print_speed_adjustment_direction);
}
} catch (std::exception &e) {
throw Slic3r::SlicingError(_(L("Error on excluded print speeds:\n") + _(L(e.what()))));
}

std::string preamble_to_put_start_layer = "";


Expand Down Expand Up @@ -5674,8 +5684,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri
return gcode;
}

double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double speed) {

double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double speed)
{
float factor = 1;
// set speed
if (speed < 0) {
Expand All @@ -5688,12 +5698,18 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double spee
speed = m_config.get_computed_value("perimeter_speed");
} else if (path.role() == erExternalPerimeter) {
speed = m_config.get_computed_value("external_perimeter_speed");
if (m_exclude_print_speeds) {
speed = m_exclude_print_speeds->adjust_speed_if_in_forbidden_range(speed);
}
} else if (path.role() == erBridgeInfill) {
speed = m_config.get_computed_value("bridge_speed");
} else if (path.role() == erInternalBridgeInfill) {
speed = m_config.get_computed_value("bridge_speed_internal");
} else if (path.role() == erOverhangPerimeter) {
speed = m_config.get_computed_value("overhangs_speed");
if (m_exclude_print_speeds) {
speed = m_exclude_print_speeds->adjust_speed_if_in_forbidden_range(speed);
}
} else if (path.role() == erInternalInfill) {
speed = m_config.get_computed_value("infill_speed");
} else if (path.role() == erSolidInfill) {
Expand Down Expand Up @@ -5770,6 +5786,7 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double spee
// Apply small perimeter 'modifier
// don't modify bridge speed
if (factor < 1 && !(is_bridge(path.role()))) {
// TODO - This does not work as expected. Needs to be fixed.
float small_speed = (float)m_config.small_perimeter_speed.get_abs_value(m_config.get_computed_value("perimeter_speed"));
if (small_speed > 0)
//apply factor between feature speed and small speed
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/GCode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "PlaceholderParser.hpp"
#include "PrintConfig.hpp"
#include "GCode/AvoidCrossingPerimeters.hpp"
#include "GCode/ExcludePrintSpeeds.hpp"
#include "GCode/CoolingBuffer.hpp"
#include "GCode/FanMover.hpp"
#include "GCode/FindReplace.hpp"
Expand Down Expand Up @@ -485,6 +486,7 @@ class GCode : ExtrusionVisitorConst {
std::unique_ptr<GCodeFindReplace> m_find_replace;
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
std::unique_ptr<ExcludePrintSpeeds> m_exclude_print_speeds;

// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done;
Expand Down
104 changes: 104 additions & 0 deletions src/libslic3r/GCode/ExcludePrintSpeeds.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "ExcludePrintSpeeds.hpp"

#include <boost/algorithm/string.hpp>
#include <algorithm>
#include <regex>

namespace Slic3r {

ExcludePrintSpeeds::ExcludePrintSpeeds(const std::string &_forbidden_ranges_user_input,
const ConfigOptionEnum<ExcludePrintSpeedsAdjustmentDirection> &_adjustment_direction)
{
adjustment_direction = _adjustment_direction;

std::vector<std::string> excluded_ranges_strings = split_user_input_ranges_to_individual_strings(_forbidden_ranges_user_input);
parse_input(excluded_ranges_strings);
check_input_correctness(excluded_ranges_strings);
}

std::vector<std::string> ExcludePrintSpeeds::split_user_input_ranges_to_individual_strings(const std::string &_excluded_ranges_user_input)
{
std::string forbidden_ranges_user_input = _excluded_ranges_user_input;
forbidden_ranges_user_input.erase(std::remove_if(forbidden_ranges_user_input.begin(),
forbidden_ranges_user_input.end(), ::isspace),
forbidden_ranges_user_input.end());

std::vector<std::string> excluded_ranges_strings;
boost::split(excluded_ranges_strings, forbidden_ranges_user_input, boost::is_any_of(","));
return excluded_ranges_strings;
}


void ExcludePrintSpeeds::parse_input(std::vector<std::string> excluded_ranges_strings)
{
auto numeric_range_regex = std::regex("^(\\d+)-(\\d+)$");
for (const auto &elem : excluded_ranges_strings) {
std::smatch regex_match;
if (!std::regex_match(elem, regex_match, numeric_range_regex)) {
throw Slic3r::SlicingError("Invalid range " + elem +
". Range must have start and end values,"
" separated by \"-\" (example: 30 - 50)");
}

auto lower_bound = std::stoi(regex_match[LOWER_BOUND_MATCH_INDEX]);
auto higher_bound = std::stoi(regex_match[UPPER_BOUND_MATCH_INDEX]);
excluded_ranges.emplace_back(lower_bound, higher_bound);
}
}

// TODO - CHKA. The strings here been whitespace-filtered. I want to print the original
// user input for HCI reasons. Not strict, might skip doing this.
void ExcludePrintSpeeds::check_input_correctness(const std::vector<std::string> &excluded_ranges_strings)
{
size_t i = 0;
for (const auto &range : excluded_ranges) {
if (range.first >= range.second) {
throw Slic3r::SlicingError("Invalid range " + excluded_ranges_strings[i] +
". Upper bound must be greater than lower bound.");
}
i++;
}

// Check range consistency (Simply check for overlap. User can enter them in non-ascending lower bound order)
std::sort(excluded_ranges.begin(), excluded_ranges.end(),
[](auto &left, auto &right) { return left.first < right.first; });

for (i = 1; i < excluded_ranges.size(); i++) {
if (excluded_ranges[i].first < excluded_ranges[i - 1].second) {
throw Slic3r::SlicingError("Ranges " + excluded_ranges_strings[i - 1] + " and " +
excluded_ranges_strings[i] + " overlap.");
}
}
}


double_t ExcludePrintSpeeds::adjust_speed_if_in_forbidden_range(double speed)
{
for (auto range : excluded_ranges) {
if (speed > range.first && speed < range.second) {
switch (adjustment_direction) {
case epsdLowest:
speed = range.first;
break;
case epsdHighest:
speed = range.second;
break;
case epsdNearest:
if ((speed - range.first) < (range.second - speed)) {
speed = range.first;
} else {
speed = range.second;
}
break;
default:
return speed;
}

return speed;
}
}

return speed;
}

}
32 changes: 32 additions & 0 deletions src/libslic3r/GCode/ExcludePrintSpeeds.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef slic3r_ExcludePrintSpeeds_hpp_
#define slic3r_ExcludePrintSpeeds_hpp_

#include "../Print.hpp"

#include <cmath>

namespace Slic3r {

class ExcludePrintSpeeds
{
private:
static constexpr size_t LOWER_BOUND_MATCH_INDEX = 1;
static constexpr size_t UPPER_BOUND_MATCH_INDEX = 2;

std::vector<std::pair<int, int>> excluded_ranges;
ConfigOptionEnum<ExcludePrintSpeedsAdjustmentDirection> adjustment_direction;

static std::vector<std::string> split_user_input_ranges_to_individual_strings(const std::string &_excluded_ranges_user_input);
void parse_input(std::vector<std::string> excluded_ranges_strings);
void check_input_correctness(const std::vector<std::string> &excluded_ranges_strings);

public:
ExcludePrintSpeeds(const std::string &_forbidden_ranges_user_input,
const ConfigOptionEnum<ExcludePrintSpeedsAdjustmentDirection> &_adjustment_direction);

double_t adjust_speed_if_in_forbidden_range(double speed);
};

}

#endif // slic3r_ExcludePrintSpeeds_hpp_
2 changes: 2 additions & 0 deletions src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ static std::vector<std::string> s_Preset_print_options {
"travel_speed", "travel_speed_z",
"max_print_speed",
"max_volumetric_speed",
"exclude_print_speed_ranges",
"exclude_print_speed_adjustment_direction",
// gapfill
"gap_fill_enabled",
"gap_fill_extension",
Expand Down
45 changes: 45 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)

static const t_config_enum_values s_keys_map_ExcludePrintSpeedsAdjustmentDirection {
{"lowest", epsdLowest},
{"highest", epsdHighest},
{"nearest", epsdNearest}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ExcludePrintSpeedsAdjustmentDirection)

static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
{
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
Expand Down Expand Up @@ -1941,6 +1948,42 @@ void PrintConfigDef::init_fff_params()
def->is_vector_extruder = true;
def->set_default_value(new ConfigOptionFloats{ 0. });

// TODO - chka: Show better feature description on mouse hover. Describe use cases and interaction with
// other features.
def = this->add("exclude_print_speed_ranges", coString);
def->label = L("Excluded speed ranges (in mm/s)");
def->tooltip = L(
"Speed ranges to be excluded. Example input form: 30-40, 60-80. "
"If any speeds fall in these ranges, they will be raised/lowered "
"according to the adjustment direction. One use case for this feature"
" is to avoid CoreXY kinematic resonances. In its current state, only speeds set by the user in the speed "
"section will be affected, not speeds set by the minimum layer time. "
"\nLeave empty to disable.");
def->mode = comExpert | comSuSi;
// def->is_vector_extruder = true;
def->set_default_value(new ConfigOptionString{""});

def = this->add("exclude_print_speed_adjustment_direction", coEnum);
def->label = L("Adjustment direction");
def->full_label = L("Exclude print speed adjustment direction");
def->category = OptionCategory::speed;
def->tooltip = L("This option decides which direction to adjust any invalid print speeds."
// "\n * None: Warn the user that print speeds in the invalid region are in effect, but do not alter them." // TODO - CHKA
"\n * Lowest: drop the speed to the lowest value of the range."
"\n * Highest: raise the speed to the highest value of the range."
"\n * Nearest: change the speed to whichever value of the above is closest to the speed set.");
def->enum_keys_map = &ConfigOptionEnum<ExcludePrintSpeedsAdjustmentDirection>::get_enum_values();
// def->enum_values.push_back("none");
def->enum_values.emplace_back("lowest");
def->enum_values.emplace_back("highest");
def->enum_values.emplace_back("nearest");
// def->enum_labels.push_back(L("None"));
def->enum_labels.emplace_back(L("Lowest"));
def->enum_labels.emplace_back(L("Highest"));
def->enum_labels.emplace_back(L("Nearest"));
def->mode = comExpert | comSuSi;
def->set_default_value(new ConfigOptionEnum<ExcludePrintSpeedsAdjustmentDirection>(epsdLowest));

def = this->add("filament_max_wipe_tower_speed", coFloats);
def->label = L("Max speed on the wipe tower");
def->tooltip = L("This setting is used to set the maximum speed when extruding inside the wipe tower (use M220)."
Expand Down Expand Up @@ -8096,6 +8139,8 @@ std::unordered_set<std::string> prusa_export_to_remove_keys = {
"default_speed",
"enforce_full_fill_volume",
// "exact_last_layer_height",
"exclude_print_speed_ranges",
"exclude_print_speed_adjustment_direction",
"external_infill_margin",
"external_perimeter_acceleration",
"external_perimeter_cut_corners",
Expand Down
8 changes: 8 additions & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ enum InfillConnection {
icConnected, icHoles, icOuterShell, icNotConnected,
};

enum ExcludePrintSpeedsAdjustmentDirection
{
epsdLowest, epsdHighest, epsdNearest
};

enum RemainingTimeType : uint8_t{
rtNone = 0,
rtM117 = 1<<0,
Expand Down Expand Up @@ -300,6 +305,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ZLiftTop)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ExcludePrintSpeedsAdjustmentDirection)

#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS

Expand Down Expand Up @@ -1287,6 +1293,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloats, wiping_volumes_extruders))
((ConfigOptionFloat, z_offset))
((ConfigOptionFloat, init_z_rotate))
((ConfigOptionString, exclude_print_speed_ranges))
((ConfigOptionEnum<ExcludePrintSpeedsAdjustmentDirection>, exclude_print_speed_adjustment_direction))

)

Expand Down
3 changes: 2 additions & 1 deletion src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
"external_perimeters_first", "external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing","external_perimeter_extrusion_change_odd_layers",
"overhangs", "perimeter_speed", "perimeter_reverse",
"seam_position", "small_perimeter_speed", "small_perimeter_min_length", " small_perimeter_max_length", "spiral_vase",
"perimeter_generator", "seam_notch_all", "seam_notch_inner", "seam_notch_outer"})
"perimeter_generator", "seam_notch_all", "seam_notch_inner", "seam_notch_outer",
"exclude_print_speed_ranges", "exclude_print_speed_adjustment_direction"})
toggle_field(el, have_perimeters);

bool has_spiral_vase = have_perimeters && config->opt_bool("spiral_vase");
Expand Down