Calculate extrusion width %s as a function of nozzle width, not layer height (#1578)

* Calculate extrusion width %s as a function of nozzle width, not layer height

* handled more width conversions

* more missing percent handling

* even more missed percent handling

* even more more extrusion % handling

* some fixes

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Jason M-H 2023-07-27 11:37:47 -04:00 committed by GitHub
parent 71ddef9724
commit be54f6bc99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 187 additions and 121 deletions

View file

@ -653,22 +653,52 @@ 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 need " << 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;
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) {
auto cofop = static_cast<const ConfigOptionFloatOrPercent*>(raw_opt);
if (cofop->value == 0 && boost::ends_with(opt_key, "_line_width")) {
return this->get_abs_value("line_width");
}
if (!cofop->percent)
return cofop->value;
cast_opt = cofop;
}
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()");
}

View file

@ -301,7 +301,7 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
bool nonempty = config.sparse_infill_density > 0;
bool has_adaptive_infill = nonempty && config.sparse_infill_pattern == ipAdaptiveCubic;
bool has_support_infill = nonempty && config.sparse_infill_pattern == ipSupportCubic;
double sparse_infill_line_width = config.sparse_infill_line_width;
double sparse_infill_line_width = config.sparse_infill_line_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,

View file

@ -75,9 +75,7 @@ Generator::Generator(const PrintObject &print_object, const std::function<void()
// 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);
//m_supporting_radius = coord_t(m_infill_extrusion_width) * 100 / coord_t(region_config.fill_density.value);
m_infill_extrusion_width = scaled<float>(region_config.sparse_infill_line_width.value);
m_infill_extrusion_width = scaled<float>(region_config.sparse_infill_line_width.get_abs_value(max_nozzle_diameter));
m_supporting_radius = coord_t(m_infill_extrusion_width) * 100 / region_config.sparse_infill_density;
const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees
@ -103,7 +101,7 @@ Generator::Generator(PrintObject* m_object, std::vector<Polygons>& contours, std
// 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.sparse_infill_line_width.value);
m_infill_extrusion_width = scaled<float>(region_config.sparse_infill_line_width.get_abs_value(max_nozzle_diameter));
//m_supporting_radius: against to the density of lightning, failures may happen if set to high density
//higher density lightning makes support harder, more time-consuming on computing and printing, but more reliable on supporting overhangs
//lower density lightning performs opposite

View file

@ -67,8 +67,6 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
{
assert(opt != nullptr);
bool first_layer = boost::starts_with(opt_key, "initial_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) {
@ -84,24 +82,18 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
opt = config.option<ConfigOptionFloatOrPercent>("line_width");
if (opt == nullptr)
throw_on_missing_variable(opt_key, "line_width");
// Use the "layer_height" instead of "initial_layer_print_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 ? "initial_layer_print_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)));
}
@ -116,18 +108,18 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResol
// This constructor builds a Flow object from an extrusion width config setting
// and other context properties.
Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloat &width, float nozzle_diameter, float height)
Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height)
{
if (height <= 0)
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
float w;
if (width.value == 0.) {
if (!width.percent && width.value <= 0.) {
// If user left option to 0, calculate a sane default width.
w = auto_extrusion_width(role, nozzle_diameter);
} else {
// If user set a manual value, use it.
w = float(width.value);
w = float(width.get_abs_value(nozzle_diameter));
}
return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height), nozzle_diameter, false);

View file

@ -98,7 +98,7 @@ public:
static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, bridge_extrusion_spacing(dmr), nozzle_diameter, true }; }
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloat &width, float nozzle_diameter, float height);
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height);
// Spacing of extrusions with rounded extrusion model.
static float rounded_rectangle_extrusion_spacing(float width, float height);

View file

@ -1783,11 +1783,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
//BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting
{
float filament_max_volumetric_speed = m_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->get_at(initial_non_support_extruder_id);
float outer_wall_line_width = print.default_region_config().outer_wall_line_width.value;
const double nozzle_diameter = m_config.nozzle_diameter.get_at(initial_non_support_extruder_id);
float outer_wall_line_width = this->config().get_abs_value("outer_wall_line_width", nozzle_diameter);
if (outer_wall_line_width == 0.0) {
float default_line_width = print.default_object_config().line_width.value;
outer_wall_line_width = default_line_width == 0.0 ? m_config.nozzle_diameter.get_at(initial_non_support_extruder_id) : default_line_width;
float default_line_width = this->config().get_abs_value("line_width", nozzle_diameter);
outer_wall_line_width = default_line_width == 0.0 ? nozzle_diameter : default_line_width;
}
Flow outer_wall_flow = Flow(outer_wall_line_width, m_config.layer_height, m_config.nozzle_diameter.get_at(initial_non_support_extruder_id));
float outer_wall_speed = print.default_region_config().outer_wall_speed.value;

View file

@ -239,7 +239,10 @@ std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Pr
int extruder_id = layerm->region().config().option("wall_filament")->getInt();
for (auto expoly : layerm->raw_slices) {
if (offset_ex(expoly, -0.2 * scale_(print.config().initial_layer_line_width)).empty())
const double nozzle_diameter = print.config().nozzle_diameter.get_at(0);
const coordf_t initial_layer_line_width = print.config().get_abs_value("initial_layer_line_width", nozzle_diameter);
if (offset_ex(expoly, -0.2 * scale_(initial_layer_line_width)).empty())
continue;
double contour_area = expoly.contour.area();
@ -279,7 +282,10 @@ std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Pr
for (auto layerm : first_layer->regions()) {
int extruder_id = layerm->region().config().option("wall_filament")->getInt();
for (auto expoly : layerm->raw_slices) {
if (offset_ex(expoly, -0.2 * scale_(object.config().line_width)).empty())
const double nozzle_diameter = object.print()->config().nozzle_diameter.get_at(0);
const coordf_t line_width = object.config().get_abs_value("line_width", nozzle_diameter);
if (offset_ex(expoly, -0.2 * scale_(line_width)).empty())
continue;
double contour_area = expoly.contour.area();

View file

@ -175,6 +175,7 @@ void Layer::make_perimeters()
&& config.filter_out_gap_fill.value == other_config.filter_out_gap_fill.value
&& config.detect_overhang_wall == other_config.detect_overhang_wall
&& config.opt_serialize("inner_wall_line_width") == other_config.opt_serialize("inner_wall_line_width")
&& config.opt_serialize("outer_wall_line_width") == other_config.opt_serialize("outer_wall_line_width")
&& config.detect_thin_wall == other_config.detect_thin_wall
//&& config.wall_infill_order == other_config.wall_infill_order
&& config.infill_wall_overlap == other_config.infill_wall_overlap

View file

@ -1545,7 +1545,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
//BBS: spacing according to width and layer height
float extrusion_spacing{ 0.f };
};
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())
@ -1554,16 +1554,18 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
// As this region may split existing regions, we collect statistics over all regions for color_idx == 0.
color_idx == 0 || config.wall_filament == int(color_idx)) {
//BBS: the extrusion line width is outer wall rather than inner wall
out.extrusion_width = std::max<float>(out.extrusion_width, float(config.outer_wall_line_width));
const double nozzle_diameter = print_object.print()->config().nozzle_diameter.get_at(0);
double outer_wall_line_width = config.get_abs_value("outer_wall_line_width", nozzle_diameter);
out.extrusion_width = std::max<float>(out.extrusion_width, outer_wall_line_width);
out.top_shell_layers = std::max<int>(out.top_shell_layers, config.top_shell_layers);
out.bottom_shell_layers = std::max<int>(out.bottom_shell_layers, config.bottom_shell_layers);
out.small_region_threshold = config.gap_infill_speed.value > 0 ?
// Gap fill enabled. Enable a single line of 1/2 extrusion width.
0.5f * float(config.outer_wall_line_width) :
0.5f * outer_wall_line_width :
// Gap fill disabled. Enable two lines slightly overlapping.
float(config.outer_wall_line_width) + 0.7f * Flow::rounded_rectangle_extrusion_spacing(float(config.outer_wall_line_width), float(layer.height));
outer_wall_line_width + 0.7f * Flow::rounded_rectangle_extrusion_spacing(outer_wall_line_width, float(layer.height));
out.small_region_threshold = scaled<float>(out.small_region_threshold * 0.5f);
out.extrusion_spacing = Flow::rounded_rectangle_extrusion_spacing(float(config.outer_wall_line_width), float(layer.height));
out.extrusion_spacing = Flow::rounded_rectangle_extrusion_spacing(float(outer_wall_line_width), float(layer.height));
++ out.num_regions;
}
assert(out.num_regions > 0);

View file

@ -1019,15 +1019,13 @@ void PerimeterGenerator::process_classic()
// get the real top surface
ExPolygons grown_lower_slices;
ExPolygons bridge_checker;
// BBS: check whether surface be bridge or not
auto nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->wall_filament - 1);
// Check whether surface be bridge or not
if (this->lower_slices != NULL) {
// BBS: get the Polygons below the polygon this layer
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, last_box);
double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width)));
// SoftFever: improve bridging
auto nozzle_diameter =
this->print_config->nozzle_diameter.get_at(this->config->wall_filament - 1);
const float bridge_margin =
std::min(float(scale_(BRIDGE_INFILL_MARGIN)),
float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4)));
@ -1047,7 +1045,8 @@ void PerimeterGenerator::process_classic()
// increase by half peri the inner space to fill the frontier between last and stored.
top_fills = union_ex(top_fills, top_polygons);
//set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap.
double infill_spacing_unscaled = this->config->sparse_infill_line_width.value;
double infill_spacing_unscaled = this->config->sparse_infill_line_width.get_abs_value(nozzle_diameter);
if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter);
fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
//ExPolygons oldLast = last;

View file

@ -1099,13 +1099,9 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
return ("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);
double extrusion_width_max = config.get_abs_value(opt_key);
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) {
@ -1297,10 +1293,10 @@ double Print::skirt_first_layer_height() const
Flow Print::brim_flow() const
{
ConfigOptionFloat width = m_config.initial_layer_line_width;
if (width.value == 0)
ConfigOptionFloatOrPercent width = m_config.initial_layer_line_width;
if (width.value <= 0)
width = m_print_regions.front()->config().inner_wall_line_width;
if (width.value == 0)
if (width.value <= 0)
width = m_objects.front()->config().line_width;
/* We currently use a random region's perimeter extruder.
@ -1310,6 +1306,7 @@ Flow Print::brim_flow() const
generation as well. */
return Flow::new_from_config_width(
frPerimeter,
// Flow::new_from_config_width takes care of the percent to value substitution
width,
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().wall_filament-1),
(float)this->skirt_first_layer_height());
@ -1317,8 +1314,8 @@ Flow Print::brim_flow() const
Flow Print::skirt_flow() const
{
ConfigOptionFloat width = m_config.initial_layer_line_width;
if (width.value == 0)
ConfigOptionFloatOrPercent width = m_config.initial_layer_line_width;
if (width.value <= 0)
width = m_objects.front()->config().line_width;
/* We currently use a random object's support material extruder.
@ -1328,6 +1325,7 @@ Flow Print::skirt_flow() const
generation as well. */
return Flow::new_from_config_width(
frPerimeter,
// Flow::new_from_config_width takes care of the percent to value substitution
width,
(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_filament-1),
(float)this->skirt_first_layer_height());

View file

@ -1057,14 +1057,17 @@ void PrintConfigDef::init_fff_params()
def->enum_labels = def_top_fill_pattern->enum_labels;
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("outer_wall_line_width", coFloat);
def = this->add("outer_wall_line_width", coFloatOrPercent);
def->label = L("Outer wall");
def->category = L("Quality");
def->tooltip = L("Line width of outer wall");
def->sidetext = L("mm");
def->tooltip = L("Line width of outer wall. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("outer_wall_speed", coFloat);
def->label = L("Outer wall");
@ -1208,15 +1211,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0.02 });
def = this->add("line_width", coFloat);
def = this->add("line_width", coFloatOrPercent);
def->label = L("Default");
def->category = L("Quality");
def->tooltip = L("Default line width if some line width is set to be zero");
def->sidetext = L("mm");
def->tooltip = L("Default line width if other line widths are set to 0. If expressed as a %, it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
def->max = 10;
def->max = 1000;
def->max_literal = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
def = this->add("reduce_fan_stop_start_freq", coBools);
def->label = L("Keep fan always on");
@ -1637,14 +1642,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(12));
def = this->add("initial_layer_line_width", coFloat);
def = this->add("initial_layer_line_width", coFloatOrPercent);
def->label = L("Initial layer");
def->category = L("Quality");
def->tooltip = L("Line width of initial layer");
def->sidetext = L("mm");
def->tooltip = L("Line width of initial layer. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("initial_layer_print_height", coFloat);
def->label = L("Initial layer height");
@ -1942,14 +1951,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("sparse_infill_line_width", coFloat);
def = this->add("sparse_infill_line_width", coFloatOrPercent);
def->label = L("Sparse infill");
def->category = L("Quality");
def->tooltip = L("Line width of internal sparse infill");
def->sidetext = L("mm");
def->tooltip = L("Line width of internal sparse infill. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("infill_wall_overlap", coPercent);
def->label = L("Infill/Wall overlap");
@ -2374,14 +2386,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("inner_wall_line_width", coFloat);
def = this->add("inner_wall_line_width", coFloatOrPercent);
def->label = L("Inner wall");
def->category = L("Quality");
def->tooltip = L("Line width of inner wall");
def->sidetext = L("mm");
def->tooltip = L("Line width of inner wall. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("inner_wall_speed", coFloat);
def->label = L("Inner wall");
@ -2773,14 +2788,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("internal_solid_infill_line_width", coFloat);
def = this->add("internal_solid_infill_line_width", coFloatOrPercent);
def->label = L("Internal solid infill");
def->category = L("Quality");
def->tooltip = L("Line width of internal solid infill");
def->sidetext = L("mm");
def->tooltip = L("Line width of internal solid infill. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("internal_solid_infill_speed", coFloat);
def->label = L("Internal solid infill");
@ -2995,14 +3013,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("support_line_width", coFloat);
def = this->add("support_line_width", coFloatOrPercent);
def->label = L("Support");
def->category = L("Quality");
def->tooltip = L("Line width of support");
def->sidetext = L("mm");
def->tooltip = L("Line width of support. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("support_interface_loop_pattern", coBool);
def->label = L("Interface use loop pattern");
@ -3318,14 +3339,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("top_surface_line_width", coFloat);
def = this->add("top_surface_line_width", coFloatOrPercent);
def->label = L("Top surface");
def->category = L("Quality");
def->tooltip = L("Line width for top surfaces");
def->sidetext = L("mm");
def->tooltip = L("Line width for top surfaces. If expressed as a %, 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 = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("top_surface_speed", coFloat);
def->label = L("Top surface");
@ -4993,7 +5017,7 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
"initial_layer_line_width" };
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
std::string key(widths[i]);
if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter) {
if (cfg.get_abs_value(key, max_nozzle_diameter) > 2.5 * max_nozzle_diameter) {
error_message.emplace(key, L("too large line width ") + std::to_string(cfg.get_abs_value(key)));
//return std::string("Too Large line width: ") + key;
}

View file

@ -643,7 +643,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, bridge_no_support))
((ConfigOptionFloat, elefant_foot_compensation))
((ConfigOptionFloat, max_bridge_length))
((ConfigOptionFloat, line_width))
((ConfigOptionFloatOrPercent, line_width))
// Force the generation of solid shells between adjacent materials/volumes.
((ConfigOptionBool, interface_shells))
((ConfigOptionFloat, layer_height))
@ -667,7 +667,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, support_bottom_z_distance))
((ConfigOptionInt, enforce_support_layers))
((ConfigOptionInt, support_filament))
((ConfigOptionFloat, support_line_width))
((ConfigOptionFloatOrPercent, support_line_width))
((ConfigOptionBool, support_interface_loop_pattern))
((ConfigOptionInt, support_interface_filament))
((ConfigOptionInt, support_interface_top_layers))
@ -727,7 +727,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, ensure_vertical_shell_thickness))
((ConfigOptionEnum<InfillPattern>, top_surface_pattern))
((ConfigOptionEnum<InfillPattern>, bottom_surface_pattern))
((ConfigOptionFloat, outer_wall_line_width))
((ConfigOptionFloatOrPercent, outer_wall_line_width))
((ConfigOptionFloat, outer_wall_speed))
((ConfigOptionFloat, infill_direction))
((ConfigOptionPercent, sparse_infill_density))
@ -737,7 +737,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, fuzzy_skin_point_distance))
((ConfigOptionFloat, gap_infill_speed))
((ConfigOptionInt, sparse_infill_filament))
((ConfigOptionFloat, sparse_infill_line_width))
((ConfigOptionFloatOrPercent, sparse_infill_line_width))
((ConfigOptionPercent, infill_wall_overlap))
((ConfigOptionFloat, sparse_infill_speed))
//BBS
@ -750,17 +750,17 @@ PRINT_CONFIG_CLASS_DEFINE(
// Detect bridging perimeters
((ConfigOptionBool, detect_overhang_wall))
((ConfigOptionInt, wall_filament))
((ConfigOptionFloat, inner_wall_line_width))
((ConfigOptionFloatOrPercent, inner_wall_line_width))
((ConfigOptionFloat, inner_wall_speed))
// Total number of perimeters.
((ConfigOptionInt, wall_loops))
((ConfigOptionFloat, minimum_sparse_infill_area))
((ConfigOptionInt, solid_infill_filament))
((ConfigOptionFloat, internal_solid_infill_line_width))
((ConfigOptionFloatOrPercent, internal_solid_infill_line_width))
((ConfigOptionFloat, internal_solid_infill_speed))
// Detect thin walls.
((ConfigOptionBool, detect_thin_wall))
((ConfigOptionFloat, top_surface_line_width))
((ConfigOptionFloatOrPercent, top_surface_line_width))
((ConfigOptionInt, top_shell_layers))
((ConfigOptionFloat, top_shell_thickness))
((ConfigOptionFloat, top_surface_speed))
@ -946,7 +946,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, travel_acceleration))
((ConfigOptionFloatOrPercent, sparse_infill_acceleration))
((ConfigOptionFloatOrPercent, internal_solid_infill_acceleration))
((ConfigOptionFloat, initial_layer_line_width))
((ConfigOptionFloatOrPercent, initial_layer_line_width))
((ConfigOptionFloat, initial_layer_print_height))
((ConfigOptionFloat, initial_layer_speed))
((ConfigOptionFloat, default_jerk))

View file

@ -423,7 +423,10 @@ static const float g_min_overhang_percent_for_lift = 0.3f;
void PrintObject::detect_overhangs_for_lift()
{
if (this->set_started(posDetectOverhangsForLift)) {
const float min_overlap = m_config.line_width * g_min_overhang_percent_for_lift;
const double nozzle_diameter = m_print->config().nozzle_diameter.get_at(0);
const coordf_t line_width = this->config().get_abs_value("line_width", nozzle_diameter);
const float min_overlap = line_width * g_min_overhang_percent_for_lift;
size_t num_layers = this->layer_count();
size_t num_raft_layers = m_slicing_params.raft_layers();
@ -433,14 +436,14 @@ void PrintObject::detect_overhangs_for_lift()
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers + 1, num_layers),
[this, min_overlap](const tbb::blocked_range<size_t>& range)
[this, min_overlap, line_width](const tbb::blocked_range<size_t>& range)
{
for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
Layer& layer = *m_layers[layer_id];
Layer& lower_layer = *layer.lower_layer;
ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap)));
layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width)));
layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(line_width), 0.1f * scale_(line_width)));
}
});
@ -2591,6 +2594,7 @@ SupportNecessaryType PrintObject::is_support_necessary()
#if 0
double threshold_rad = (m_config.support_threshold_angle.value < EPSILON ? 30 : m_config.support_threshold_angle.value + 1) * M_PI / 180.;
int enforce_support_layers = m_config.enforce_support_layers;
// not fixing in extrusion width % PR b/c never called
const coordf_t extrusion_width = m_config.line_width.value;
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
float max_bridge_length = scale_(m_config.max_bridge_length.value);

View file

@ -21,7 +21,7 @@ unsigned int PrintRegion::extruder(FlowRole role) const
Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const
{
const PrintConfig &print_config = object.print()->config();
ConfigOptionFloat config_width;
ConfigOptionFloatOrPercent config_width;
// Get extrusion width from configuration.
// (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && print_config.initial_layer_line_width.value > 0) {

View file

@ -2252,7 +2252,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// check if the sharp tails should be extended higher
bool detect_first_sharp_tail_only = false;
const coordf_t extrusion_width = m_object_config->line_width.value;
const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(object.print()->config().nozzle_diameter.get_at(object.config().support_interface_filament-1));
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
if (is_auto(m_object_config->support_type.value) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) {
for (size_t layer_nr = 0; layer_nr < object.layer_count(); layer_nr++) {
@ -2345,7 +2345,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// BBS group overhang clusters
if (g_config_remove_small_overhangs) {
std::vector<OverhangCluster> clusters;
double fw_scaled = scale_(m_object_config->line_width);
double fw_scaled = scale_(extrusion_width);
std::set<ExPolygon*> removed_overhang;
for (size_t layer_id = layer_id_start; layer_id < num_layers; layer_id++) {

View file

@ -689,7 +689,12 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
m_object_config->support_interface_pattern == smipConcentric ?
ipConcentric :
(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width;
const auto nozzle_diameter = object.print()->config().nozzle_diameter.get_at(object.config().support_interface_filament-1);
const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(nozzle_diameter);
const coordf_t support_extrusion_width = m_object_config->support_line_width.get_abs_value(nozzle_diameter);
m_support_params.support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width;
is_slim = is_tree_slim(support_type, support_style);
is_strong = is_tree(support_type) && support_style == smsTreeStrong;
MAX_BRANCH_RADIUS = 10.0;
@ -723,7 +728,8 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
const PrintObjectConfig& config = m_object->config();
SupportType stype = support_type;
const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution;
const coordf_t extrusion_width = config.line_width.value;
const double nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(0);
const coordf_t extrusion_width = config.get_abs_value("line_width", nozzle_diameter);
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
const bool bridge_no_support = max_bridge_length > 0;
@ -2011,7 +2017,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
const size_t top_interface_layers = config.support_interface_top_layers.value;
const size_t bottom_interface_layers = config.support_interface_bottom_layers.value;
const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.);// * layer_height / branch_radius; //Scale factor per layer to produce the desired angle.
const coordf_t line_width = config.support_line_width;
const double nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(0);
const coordf_t line_width = config.get_abs_value("support_line_width", nozzle_diameter);
const coordf_t line_width_scaled = scale_(line_width);
const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning;
@ -2472,12 +2479,14 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
const size_t bottom_interface_layers = config.support_interface_bottom_layers.value;
const size_t top_interface_layers = config.support_interface_top_layers.value;
float DO_NOT_MOVER_UNDER_MM = is_slim ? 0 : 5; // do not move contact points under 5mm
const auto nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(m_object->config().support_interface_filament-1);
const auto support_line_width = config.support_line_width.get_abs_value(nozzle_diameter);
auto get_branch_angle = [this,&config](coordf_t radius) {
if (config.tree_support_branch_angle.value < 30.0) return config.tree_support_branch_angle.value;
return (radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0;
};
auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width](const Node *node, int power = 1) {
auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width, support_line_width](const Node *node, int power = 1) {
double move_dist = node->max_move_dist;
if (node->max_move_dist == 0) {
if (node->radius == 0) node->radius = calc_branch_radius(branch_radius, node->dist_mm_to_top, diameter_angle_scale_factor);
@ -2485,7 +2494,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS)
angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0;
double tan_angle = tan(angle * M_PI / 180);
int wall_count_ = node->radius > 2 * config.support_line_width ? wall_count : 1;
int wall_count_ = node->radius > 2 * support_line_width ? wall_count : 1;
node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) * wall_count_ : std::numeric_limits<coordf_t>::max();
node->max_move_dist = std::min(node->max_move_dist, support_extrusion_width);
move_dist = node->max_move_dist;

View file

@ -166,8 +166,8 @@ public:
protected:
double speed_first_layer() const { return m_config.option<ConfigOptionFloat>("initial_layer_speed")->value; };
double speed_perimeter() const { return m_config.option<ConfigOptionFloat>("outer_wall_speed")->value; };
double line_width_first_layer() const { return m_config.option<ConfigOptionFloat>("initial_layer_line_width")->value; };
double line_width() const { return m_config.option<ConfigOptionFloat>("line_width")->value; };
double line_width_first_layer() const { return m_config.get_abs_value("initial_layer_line_width"); };
double line_width() const { return m_config.get_abs_value("line_width"); };
int wall_count() const { return m_config.option<ConfigOptionInt>("wall_loops")->value; };
private:

View file

@ -1390,7 +1390,7 @@ void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) {
char buf[64]; // locales don't matter here (sprintf/atof)
sprintf(buf, "%.2lf", dmr * opt_nozzle->values.front() / 0.4);
config.set_key_value(key, new ConfigOptionFloat(atof(buf)));
config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf),false));
};
set_extrusion_width("support_line_width", 0.35);

View file

@ -1,4 +1,5 @@
#include "Plater.hpp"
#include "libslic3r/Config.hpp"
#include <cstddef>
#include <algorithm>
@ -8155,7 +8156,7 @@ void Plater::_calib_pa_pattern(const Calib_Params& params)
double nozzle_diameter = printer_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0);
print_config.set_key_value(
opt.first,
new ConfigOptionFloat(nozzle_diameter * opt.second / 100)
new ConfigOptionFloatOrPercent(nozzle_diameter * opt.second / 100, false)
);
}
@ -8333,8 +8334,8 @@ void Plater::calib_flowrate(int pass) {
_obj->config.set_key_value("detect_thin_wall", new ConfigOptionBool(true));
_obj->config.set_key_value("filter_out_gap_fill", new ConfigOptionFloat(0));
_obj->config.set_key_value("sparse_infill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear));
_obj->config.set_key_value("top_surface_line_width", new ConfigOptionFloat(nozzle_diameter * 1.2f));
_obj->config.set_key_value("internal_solid_infill_line_width", new ConfigOptionFloat(nozzle_diameter * 1.2f));
_obj->config.set_key_value("top_surface_line_width", new ConfigOptionFloatOrPercent(nozzle_diameter * 1.2f, false));
_obj->config.set_key_value("internal_solid_infill_line_width", new ConfigOptionFloatOrPercent(nozzle_diameter * 1.2f, false));
_obj->config.set_key_value("top_surface_pattern", new ConfigOptionEnum<InfillPattern>(ipMonotonic));
_obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f));
_obj->config.set_key_value("top_surface_pattern", new ConfigOptionEnum<InfillPattern>(ipMonotonic));
@ -8455,7 +8456,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
print_config->set_key_value("outer_wall_line_width", new ConfigOptionFloat(line_width));
print_config->set_key_value("outer_wall_line_width", new ConfigOptionFloatOrPercent(line_width, false));
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
print_config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
obj->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterAndInner));