Skip to content

Commit 64607ce

Browse files
committed
feat(flow): Calculate extrusion width from nozzle diameter
Ported from SuperSlicer (Remi Durand @supermerill)
1 parent 5dc277b commit 64607ce

10 files changed

Lines changed: 104 additions & 54 deletions

File tree

src/libslic3r/Config.cpp

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
///|/ Copyright (c) 2012 Simon George
1616
///|/ Copyright (c) 2012 Johannes Reinhardt
1717
///|/ Copyright (c) 2011 Clarence Risher
18+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
1819
///|/
1920
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
2021
///|/
@@ -596,23 +597,53 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
596597
{
597598
// Get stored option value.
598599
const ConfigOption *raw_opt = this->option(opt_key);
600+
if (raw_opt == nullptr) {
601+
std::stringstream ss;
602+
ss << "You can't define an option that needs " << opt_key << " without defining it!";
603+
throw std::runtime_error(ss.str());
604+
}
599605
assert(raw_opt != nullptr);
606+
600607
if (raw_opt->type() == coFloat)
601-
return static_cast<const ConfigOptionFloat*>(raw_opt)->value;
608+
return static_cast<const ConfigOptionFloat *>(raw_opt)->value;
609+
if (raw_opt->type() == coInt)
610+
return static_cast<const ConfigOptionInt *>(raw_opt)->value;
611+
if (raw_opt->type() == coBool)
612+
return static_cast<const ConfigOptionBool *>(raw_opt)->value ? 1 : 0;
613+
614+
const ConfigOptionPercent *cast_opt = nullptr;
602615
if (raw_opt->type() == coFloatOrPercent) {
603-
// Get option definition.
604-
const ConfigDef *def = this->def();
605-
if (def == nullptr)
606-
throw NoDefinitionException(opt_key);
607-
const ConfigOptionDef *opt_def = def->get(opt_key);
608-
assert(opt_def != nullptr);
609-
// Compute absolute value over the absolute value of the base option.
610-
//FIXME there are some ratio_over chains, which end with empty ratio_with.
611-
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
612-
return opt_def->ratio_over.empty() ? 0. :
613-
static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
616+
auto cofop = static_cast<const ConfigOptionFloatOrPercent *>(raw_opt);
617+
if (cofop->value == 0 && boost::ends_with(opt_key, "_extrusion_width")) {
618+
return this->get_abs_value("extrusion_width");
619+
}
620+
if (!cofop->percent)
621+
return cofop->value;
622+
cast_opt = cofop;
614623
}
615-
throw ConfigurationError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
624+
625+
if (raw_opt->type() == coPercent) {
626+
cast_opt = static_cast<const ConfigOptionPercent *>(raw_opt);
627+
}
628+
629+
// Get option definition.
630+
const ConfigDef *def = this->def();
631+
if (def == nullptr)
632+
throw NoDefinitionException(opt_key);
633+
const ConfigOptionDef *opt_def = def->get(opt_key);
634+
635+
assert(opt_def != nullptr);
636+
if (opt_def->ratio_over == "")
637+
return cast_opt->get_abs_value(1);
638+
639+
// Compute absolute value over the absolute value of the base option.
640+
// FIXME there are some ratio_over chains, which end with empty ratio_with.
641+
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
642+
if (opt_def->ratio_over.empty())
643+
throw ConfigurationError("ConfigBase::get_abs_value(): ratio_over is empty for option " + opt_key);
644+
return static_cast<const ConfigOptionFloatOrPercent *>(raw_opt)->get_abs_value(
645+
this->get_abs_value(opt_def->ratio_over)
646+
);
616647
}
617648

618649
// Return an absolute value of a possibly relative config variable.

src/libslic3r/Fill/FillAdaptive.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas
2+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
23
///|/
34
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
45
///|/
@@ -314,7 +315,7 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
314315
bool nonempty = config.fill_density > 0;
315316
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
316317
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
317-
double infill_extrusion_width = config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * config.infill_extrusion_width : config.infill_extrusion_width;
318+
double infill_extrusion_width = config.infill_extrusion_width.get_abs_value(max_nozzle_diameter);
318319
region_fill_data.push_back(RegionFillData({
319320
has_adaptive_infill ? Tristate::Maybe : Tristate::No,
320321
has_support_infill ? Tristate::Maybe : Tristate::No,

src/libslic3r/Fill/Lightning/Generator.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ Generator::Generator(const PrintObject &print_object, const coordf_t fill_densit
5050
// Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account.
5151
const double layer_thickness = scaled<double>(object_config.layer_height.value);
5252

53-
m_infill_extrusion_width = scaled<float>(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width :
54-
region_config.infill_extrusion_width != 0. ? region_config.infill_extrusion_width :
55-
default_infill_extrusion_width);
53+
m_infill_extrusion_width = scaled<float>(region_config.infill_extrusion_width.get_abs_value(max_nozzle_diameter));
5654
m_supporting_radius = coord_t(m_infill_extrusion_width * 100. / fill_density);
5755

5856
const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees

src/libslic3r/Flow.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
///|/ ported from lib/Slic3r/Flow.pm:
66
///|/ Copyright (c) Prusa Research 2022 Vojtěch Bubník @bubnikv
77
///|/ Copyright (c) Slic3r 2012 - 2014 Alessandro Ranellucci @alranel
8+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
89
///|/
910
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
1011
///|/
@@ -78,8 +79,6 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
7879
{
7980
assert(opt != nullptr);
8081

81-
bool first_layer = boost::starts_with(opt_key, "first_layer_");
82-
8382
#if 0
8483
// This is the logic used for skit / brim, but not for the rest of the 1st layer.
8584
if (opt->value == 0. && first_layer) {
@@ -95,24 +94,18 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
9594
opt = config.option<ConfigOptionFloatOrPercent>("extrusion_width");
9695
if (opt == nullptr)
9796
throw_on_missing_variable(opt_key, "extrusion_width");
98-
// Use the "layer_height" instead of "first_layer_height".
99-
first_layer = false;
10097
}
10198

99+
auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
100+
if (opt_nozzle_diameters == nullptr)
101+
throw_on_missing_variable(opt_key, "nozzle_diameter");
102+
102103
if (opt->percent) {
103-
auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height";
104-
auto opt_layer_height = config.option(opt_key_layer_height);
105-
if (opt_layer_height == nullptr)
106-
throw_on_missing_variable(opt_key, opt_key_layer_height);
107-
assert(! first_layer || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent);
108-
return opt->get_abs_value(opt_layer_height->getFloat());
104+
return opt->get_abs_value(float(opt_nozzle_diameters->get_at(first_printing_extruder)));
109105
}
110106

111107
if (opt->value == 0.) {
112108
// If user left option to 0, calculate a sane default width.
113-
auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
114-
if (opt_nozzle_diameters == nullptr)
115-
throw_on_missing_variable(opt_key, "nozzle_diameter");
116109
return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder)));
117110
}
118111

@@ -138,7 +131,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
138131
w = auto_extrusion_width(role, nozzle_diameter);
139132
} else {
140133
// If user set a manual value, use it.
141-
w = float(width.get_abs_value(height));
134+
w = float(width.get_abs_value(nozzle_diameter));
142135
}
143136

144137
return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height), nozzle_diameter, false);

src/libslic3r/Layer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
///|/ ported from lib/Slic3r/Layer.pm:
66
///|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv
77
///|/ Copyright (c) Slic3r 2011 - 2016 Alessandro Ranellucci @alranel
8+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
89
///|/
910
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
1011
///|/
@@ -656,7 +657,8 @@ inline bool has_compatible_layer_regions(const PrintRegionConfig &config, const
656657
config.external_perimeter_speed == other_config.external_perimeter_speed &&
657658
(config.gap_fill_enabled ? config.gap_fill_speed.value : 0.) == (other_config.gap_fill_enabled ? other_config.gap_fill_speed.value : 0.) &&
658659
config.overhangs == other_config.overhangs &&
659-
config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width") &&
660+
config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width") &&
661+
config.opt_serialize("external_perimeter_extrusion_width") == other_config.opt_serialize("external_perimeter_extrusion_width") &&
660662
config.thin_walls == other_config.thin_walls &&
661663
config.external_perimeters_first == other_config.external_perimeters_first &&
662664
config.infill_overlap == other_config.infill_overlap &&

src/libslic3r/MultiMaterialSegmentation.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
///|/ Copyright (c) Prusa Research 2021 - 2023 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
2+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
23
///|/
34
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
45
///|/
@@ -982,7 +983,7 @@ static inline std::vector<std::vector<ExPolygons>> segmentation_top_and_bottom_l
982983
// Maximum number of bottom layers for a queried color.
983984
int bottom_solid_layers { 0 };
984985
};
985-
auto layer_color_stat = [&layers = std::as_const(layers)](const size_t layer_idx, const size_t color_idx) -> LayerColorStat {
986+
auto layer_color_stat = [&layers = std::as_const(layers), &print_object](const size_t layer_idx, const size_t color_idx) -> LayerColorStat {
986987
LayerColorStat out;
987988
const Layer &layer = *layers[layer_idx];
988989
for (const LayerRegion *region : layer.regions())
@@ -2296,4 +2297,4 @@ std::vector<std::vector<ExPolygons>> fuzzy_skin_segmentation_by_painting(const P
22962297
}
22972298

22982299

2299-
} // namespace Slic3r
2300+
} // namespace Slic3r

src/libslic3r/Print.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
1818
///|/ Copyright (c) 2012 Michael Moon
1919
///|/ Copyright (c) 2011 Richard Goodwin
20+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
2021
///|/
2122
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
2223
///|/
@@ -671,13 +672,9 @@ std::string Print::validate(std::vector<std::string>* warnings) const
671672
return _u8L("One or more object were assigned an extruder that the printer does not have.");
672673
#endif
673674

674-
auto validate_extrusion_width = [/*min_nozzle_diameter,*/ max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool {
675-
// This may change in the future, if we switch to "extrusion width wrt. nozzle diameter"
676-
// instead of currently used logic "extrusion width wrt. layer height", see GH issues #1923 #2829.
677-
// double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
678-
// double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
679-
double extrusion_width_min = config.get_abs_value(opt_key, layer_height);
680-
double extrusion_width_max = extrusion_width_min;
675+
auto validate_extrusion_width = [min_nozzle_diameter, max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool {
676+
double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
677+
double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
681678
if (extrusion_width_min == 0) {
682679
// Default "auto-generated" extrusion width is always valid.
683680
} else if (extrusion_width_min <= layer_height) {

src/libslic3r/PrintConfig.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
///|/ Copyright (c) OrcaSlicer 2023 SoftFever @SoftFever
2121
///|/ Copyright (c) 2024 Vovodroid @Vovodroid
2222
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
23+
///|/ Copyright (c) SuperSlicer 2020 Remi Durand @supermerill
2324
///|/
2425
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
2526
///|/
@@ -1202,12 +1203,14 @@ void PrintConfigDef::init_fff_params()
12021203
def->category = L("Extrusion Width");
12031204
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for external perimeters. "
12041205
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
1205-
"If expressed as percentage (for example 200%), it will be computed over layer height.");
1206+
"If expressed as percentage (for example 200%), it will be computed over the nozzle diameter.");
12061207
def->sidetext = L("mm or %");
1208+
def->ratio_over = "nozzle_diameter";
12071209
def->min = 0;
1210+
def->max = 1000;
12081211
def->max_literal = 50;
12091212
def->mode = comAdvanced;
1210-
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
1213+
def->set_default_value(new ConfigOptionFloatOrPercent(105, true));
12111214

12121215
def = this->add("external_perimeter_speed", coFloatOrPercent);
12131216
def->label = L("External perimeters");
@@ -1326,8 +1329,9 @@ void PrintConfigDef::init_fff_params()
13261329
def->tooltip = L("Set this to a non-zero value to allow a manual extrusion width. "
13271330
"If left to zero, Slic3r derives extrusion widths from the nozzle diameter "
13281331
"(see the tooltips for perimeter extrusion width, infill extrusion width etc). "
1329-
"If expressed as percentage (for example: 230%), it will be computed over layer height.");
1332+
"If expressed as percentage (for example: 230%), it will be computed over the nozzle diameter.");
13301333
def->sidetext = L("mm or %");
1334+
def->ratio_over = "nozzle_diameter";
13311335
def->min = 0;
13321336
def->max = 1000;
13331337
def->max_literal = 50;
@@ -1789,14 +1793,15 @@ void PrintConfigDef::init_fff_params()
17891793
def->category = L("Extrusion Width");
17901794
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for first layer. "
17911795
"You can use this to force fatter extrudates for better adhesion. If expressed "
1792-
"as percentage (for example 120%) it will be computed over first layer height. "
1796+
"as percentage (for example 120%) it will be computed over the nozzle diameter "
17931797
"If set to zero, it will use the default extrusion width.");
17941798
def->sidetext = L("mm or %");
1795-
def->ratio_over = "first_layer_height";
1799+
def->ratio_over = "nozzle_diameter";
17961800
def->min = 0;
1801+
def->max = 1000;
17971802
def->max_literal = 50;
17981803
def->mode = comAdvanced;
1799-
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
1804+
def->set_default_value(new ConfigOptionFloatOrPercent(140, true));
18001805

18011806
def = this->add("first_layer_height", coFloatOrPercent);
18021807
def->label = L("First layer height");
@@ -2209,9 +2214,11 @@ void PrintConfigDef::init_fff_params()
22092214
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill. "
22102215
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
22112216
"You may want to use fatter extrudates to speed up the infill and make your parts stronger. "
2212-
"If expressed as percentage (for example 90%) it will be computed over layer height.");
2217+
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
22132218
def->sidetext = L("mm or %");
2219+
def->ratio_over = "nozzle_diameter";
22142220
def->min = 0;
2221+
def->max = 1000;
22152222
def->max_literal = 50;
22162223
def->mode = comAdvanced;
22172224
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
@@ -3022,10 +3029,12 @@ void PrintConfigDef::init_fff_params()
30223029
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for perimeters. "
30233030
"You may want to use thinner extrudates to get more accurate surfaces. "
30243031
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
3025-
"If expressed as percentage (for example 200%) it will be computed over layer height.");
3032+
"If expressed as percentage (for example 200%) it will be computed over the nozzle diameter.");
30263033
def->sidetext = L("mm or %");
30273034
def->aliases = { "perimeters_extrusion_width" };
3035+
def->ratio_over = "nozzle_diameter";
30283036
def->min = 0;
3037+
def->max = 1000;
30293038
def->max_literal = 50;
30303039
def->mode = comAdvanced;
30313040
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
@@ -3525,9 +3534,11 @@ void PrintConfigDef::init_fff_params()
35253534
def->category = L("Extrusion Width");
35263535
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. "
35273536
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
3528-
"If expressed as percentage (for example 90%) it will be computed over layer height.");
3537+
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
35293538
def->sidetext = L("mm or %");
3539+
def->ratio_over = "nozzle_diameter";
35303540
def->min = 0;
3541+
def->max = 1000;
35313542
def->max_literal = 50;
35323543
def->mode = comAdvanced;
35333544
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
@@ -3813,9 +3824,11 @@ void PrintConfigDef::init_fff_params()
38133824
def->category = L("Extrusion Width");
38143825
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for support material. "
38153826
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
3816-
"If expressed as percentage (for example 90%) it will be computed over layer height.");
3827+
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
38173828
def->sidetext = L("mm or %");
3829+
def->ratio_over = "nozzle_diameter";
38183830
def->min = 0;
3831+
def->max = 1000;
38193832
def->max_literal = 50;
38203833
def->mode = comAdvanced;
38213834
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
@@ -4149,12 +4162,14 @@ void PrintConfigDef::init_fff_params()
41494162
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. "
41504163
"You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. "
41514164
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
4152-
"If expressed as percentage (for example 90%) it will be computed over layer height.");
4165+
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
41534166
def->sidetext = L("mm or %");
4167+
def->ratio_over = "nozzle_diameter";
41544168
def->min = 0;
4169+
def->max = 1000;
41554170
def->max_literal = 50;
41564171
def->mode = comAdvanced;
4157-
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
4172+
def->set_default_value(new ConfigOptionFloatOrPercent(105, true));
41584173

41594174
def = this->add("top_solid_infill_speed", coFloatOrPercent);
41604175
def->label = L("Top solid infill");

0 commit comments

Comments
 (0)