Skip to content

Commit

Permalink
[FEATURE] Calculate extrusion width based on nozzle diameter
Browse files Browse the repository at this point in the history
  • Loading branch information
mjonuschat committed May 13, 2024
1 parent f43e5f8 commit 4fa385c
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 62 deletions.
60 changes: 47 additions & 13 deletions src/libslic3r/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,23 +692,57 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
{
// Get stored option value.
const ConfigOption *raw_opt = this->option(opt_key);
if (raw_opt == nullptr) {
std::stringstream ss;
ss << "You can't define an option that needs " << opt_key << " without defining it!";
throw std::runtime_error(ss.str());
}
assert(raw_opt != nullptr);

if (raw_opt->type() == coFloat)
return static_cast<const ConfigOptionFloat*>(raw_opt)->value;
return static_cast<const ConfigOptionFloat *>(raw_opt)->value;
if (raw_opt->type() == coInt)
return static_cast<const ConfigOptionInt *>(raw_opt)->value;
if (raw_opt->type() == coBool)
return static_cast<const ConfigOptionBool *>(raw_opt)->value ? 1 : 0;

const ConfigOptionPercent *cast_opt = nullptr;
if (raw_opt->type() == coFloatOrPercent) {
// Get option definition.
const ConfigDef *def = this->def();
if (def == nullptr)
throw NoDefinitionException(opt_key);
const ConfigOptionDef *opt_def = def->get(opt_key);
assert(opt_def != nullptr);
// Compute absolute value over the absolute value of the base option.
//FIXME there are some ratio_over chains, which end with empty ratio_with.
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
return opt_def->ratio_over.empty() ? 0. :
static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
auto cofop = static_cast<const ConfigOptionFloatOrPercent *>(raw_opt);
if (cofop->value == 0 && boost::ends_with(opt_key, "_extrusion_width")) {
return this->get_abs_value("extrusion_width");
}
if (!cofop->percent)
return cofop->value;
cast_opt = cofop;
}
throw ConfigurationError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");

if (raw_opt->type() == coPercent) {
cast_opt = static_cast<const ConfigOptionPercent *>(raw_opt);
}

// Get option definition.
const ConfigDef *def = this->def();
if (def == nullptr)
throw NoDefinitionException(opt_key);
const ConfigOptionDef *opt_def = def->get(opt_key);

assert(opt_def != nullptr);
if (opt_def->ratio_over == "")
return cast_opt->get_abs_value(1);

// Compute absolute value over the absolute value of the base option.
// FIXME there are some ratio_over chains, which end with empty ratio_with.
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
return opt_def->ratio_over.empty() ?
0. :
static_cast<const ConfigOptionFloatOrPercent *>(raw_opt)->get_abs_value(
this->get_abs_value(opt_def->ratio_over)
);

throw ConfigurationError(
"ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"
);
}

// Return an absolute value of a possibly relative config variable.
Expand Down
2 changes: 1 addition & 1 deletion src/libslic3r/Fill/FillAdaptive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
bool nonempty = config.fill_density > 0;
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
double infill_extrusion_width = config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * config.infill_extrusion_width : config.infill_extrusion_width;
double infill_extrusion_width = config.infill_extrusion_width.get_abs_value(max_nozzle_diameter);
region_fill_data.push_back(RegionFillData({
has_adaptive_infill ? Tristate::Maybe : Tristate::No,
has_support_infill ? Tristate::Maybe : Tristate::No,
Expand Down
4 changes: 1 addition & 3 deletions src/libslic3r/Fill/Lightning/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ Generator::Generator(const PrintObject &print_object, const coordf_t fill_densit
// 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.
const double layer_thickness = scaled<double>(object_config.layer_height.value);

m_infill_extrusion_width = scaled<float>(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width :
region_config.infill_extrusion_width != 0. ? region_config.infill_extrusion_width :
default_infill_extrusion_width);
m_infill_extrusion_width = m_infill_extrusion_width = scaled<float>(region_config.infill_extrusion_width.get_abs_value(max_nozzle_diameter));
m_supporting_radius = coord_t(m_infill_extrusion_width * 100. / fill_density);

const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees
Expand Down
20 changes: 6 additions & 14 deletions src/libslic3r/Flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
{
assert(opt != nullptr);

bool first_layer = boost::starts_with(opt_key, "first_layer_");

#if 0
// This is the logic used for skit / brim, but not for the rest of the 1st layer.
if (opt->value == 0. && first_layer) {
Expand All @@ -90,24 +88,18 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
opt = config.option<ConfigOptionFloatOrPercent>("extrusion_width");
if (opt == nullptr)
throw_on_missing_variable(opt_key, "extrusion_width");
// Use the "layer_height" instead of "first_layer_height".
first_layer = false;
}

auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
if (opt_nozzle_diameters == nullptr)
throw_on_missing_variable(opt_key, "nozzle_diameter");

if (opt->percent) {
auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height";
auto opt_layer_height = config.option(opt_key_layer_height);
if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, opt_key_layer_height);
assert(! first_layer || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent);
return opt->get_abs_value(opt_layer_height->getFloat());
return opt->get_abs_value(float(opt_nozzle_diameters->get_at(first_printing_extruder)));
}

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

Expand All @@ -133,7 +125,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
w = auto_extrusion_width(role, nozzle_diameter);
} else {
// If user set a manual value, use it.
w = float(width.get_abs_value(height));
w = float(width.get_abs_value(nozzle_diameter));
}

return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height), nozzle_diameter, false);
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@ void Layer::make_perimeters()
(other_config.gap_fill_enabled ? other_config.gap_fill_speed.value : 0.)
&& config.overhangs == other_config.overhangs
&& config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width")
&& config.opt_serialize("external_perimeter_extrusion_width") == other_config.opt_serialize("external_perimeter_extrusion_width")
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first
&& config.infill_overlap == other_config.infill_overlap
Expand Down
10 changes: 6 additions & 4 deletions src/libslic3r/MultiMaterialSegmentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,22 +996,24 @@ static inline std::vector<std::vector<ExPolygons>> mm_segmentation_top_and_botto
// Maximum number of bottom layers for a queried color.
int bottom_solid_layers { 0 };
};
auto layer_color_stat = [&layers = std::as_const(layers)](const size_t layer_idx, const size_t color_idx) -> LayerColorStat {
auto layer_color_stat = [&layers = std::as_const(layers), &print_object](const size_t layer_idx, const size_t color_idx) -> LayerColorStat {
LayerColorStat out;
const Layer &layer = *layers[layer_idx];
for (const LayerRegion *region : layer.regions())
if (const PrintRegionConfig &config = region->region().config();
// color_idx == 0 means "don't know" extruder aka the underlying extruder.
// As this region may split existing regions, we collect statistics over all regions for color_idx == 0.
color_idx == 0 || config.perimeter_extruder == int(color_idx)) {
out.extrusion_width = std::max<float>(out.extrusion_width, float(config.perimeter_extrusion_width));
const double nozzle_diameter = print_object.print()->config().nozzle_diameter.get_at(0);
double perimeter_extrusion_width = config.get_abs_value("perimeter_extrusion_width", nozzle_diameter);
out.extrusion_width = std::max<float>(out.extrusion_width, perimeter_extrusion_width);
out.top_solid_layers = std::max<int>(out.top_solid_layers, config.top_solid_layers);
out.bottom_solid_layers = std::max<int>(out.bottom_solid_layers, config.bottom_solid_layers);
out.small_region_threshold = config.gap_fill_enabled.value && config.gap_fill_speed.value > 0 ?
// Gap fill enabled. Enable a single line of 1/2 extrusion width.
0.5f * float(config.perimeter_extrusion_width) :
0.5f * perimeter_extrusion_width :
// Gap fill disabled. Enable two lines slightly overlapping.
float(config.perimeter_extrusion_width) + 0.7f * Flow::rounded_rectangle_extrusion_spacing(float(config.perimeter_extrusion_width), float(layer.height));
perimeter_extrusion_width + 0.7f * Flow::rounded_rectangle_extrusion_spacing(perimeter_extrusion_width, float(layer.height));
out.small_region_threshold = scaled<float>(out.small_region_threshold * 0.5f);
++ out.num_regions;
}
Expand Down
10 changes: 3 additions & 7 deletions src/libslic3r/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,13 +724,9 @@ std::string Print::validate(std::vector<std::string>* warnings) const
return _u8L("One or more object were assigned an extruder that the printer does not have.");
#endif

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 {
// This may change in the future, if we switch to "extrusion width wrt. nozzle diameter"
// instead of currently used logic "extrusion width wrt. layer height", see GH issues #1923 #2829.
// double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
// double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
double extrusion_width_min = config.get_abs_value(opt_key, layer_height);
double extrusion_width_max = extrusion_width_min;
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 {
double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
if (extrusion_width_min == 0) {
// Default "auto-generated" extrusion width is always valid.
} else if (extrusion_width_min <= layer_height) {
Expand Down
54 changes: 34 additions & 20 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -980,12 +980,14 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extrusion Width");
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for external perimeters. "
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
"If expressed as percentage (for example 200%), it will be computed over layer height.");
"If expressed as percentage (for example 200%), it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
def->set_default_value(new ConfigOptionFloatOrPercent(105, true));

def = this->add("external_perimeter_speed", coFloatOrPercent);
def->label = L("External perimeters");
Expand Down Expand Up @@ -1104,11 +1106,12 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Set this to a non-zero value to allow a manual extrusion width. "
"If left to zero, Slic3r derives extrusion widths from the nozzle diameter "
"(see the tooltips for perimeter extrusion width, infill extrusion width etc). "
"If expressed as percentage (for example: 230%), it will be computed over layer height.");
"If expressed as percentage (for example: 230%), it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max = 1000;
def->max_literal = 50;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));

Expand Down Expand Up @@ -1527,14 +1530,15 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extrusion Width");
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for first layer. "
"You can use this to force fatter extrudates for better adhesion. If expressed "
"as percentage (for example 120%) it will be computed over first layer height. "
"as percentage (for example 120%) it will be computed over the nozzle diameter "
"If set to zero, it will use the default extrusion width.");
def->sidetext = L("mm or %");
def->ratio_over = "first_layer_height";
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
def->set_default_value(new ConfigOptionFloatOrPercent(140, true));

def = this->add("first_layer_height", coFloatOrPercent);
def->label = L("First layer height");
Expand Down Expand Up @@ -1903,10 +1907,12 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill. "
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
"You may want to use fatter extrudates to speed up the infill and make your parts stronger. "
"If expressed as percentage (for example 90%) it will be computed over layer height.");
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));

Expand Down Expand Up @@ -2637,11 +2643,13 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for perimeters. "
"You may want to use thinner extrudates to get more accurate surfaces. "
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
"If expressed as percentage (for example 200%) it will be computed over layer height.");
"If expressed as percentage (for example 200%) it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->aliases = { "perimeters_extrusion_width" };
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));

Expand Down Expand Up @@ -3060,10 +3068,12 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extrusion Width");
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. "
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
"If expressed as percentage (for example 90%) it will be computed over layer height.");
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));

Expand Down Expand Up @@ -3329,10 +3339,12 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extrusion Width");
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for support material. "
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
"If expressed as percentage (for example 90%) it will be computed over layer height.");
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));

Expand Down Expand Up @@ -3650,12 +3662,14 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. "
"You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. "
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
"If expressed as percentage (for example 90%) it will be computed over layer height.");
"If expressed as percentage (for example 90%) it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max_literal = 50;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
def->set_default_value(new ConfigOptionFloatOrPercent(105, true));

def = this->add("top_solid_infill_speed", coFloatOrPercent);
def->label = L("Top solid infill");
Expand Down
Loading

0 comments on commit 4fa385c

Please sign in to comment.