[Feature] Introduced option to control amount of internal bridging, fixing internal bridge missing for some sloped surfaces (#3319)

* ENH: Improve internal bridge detection for sloped surfaces

* Moved lightning detection out of the parallel threads

* Naming conventions

* Revised approach - use reduced expansion multipliers

* Further reduce filtering, flagged option as experimental

* Corrected code comment

* Updated tool tip

* Introduced filtering drop down option
This commit is contained in:
Ioannis Giannakas 2024-01-13 17:20:08 +02:00 committed by GitHub
parent b4925363d6
commit f7b92d9813
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 38 deletions

View file

@ -786,7 +786,7 @@ static std::vector<std::string> s_Preset_print_options {
"independent_support_layer_height", "independent_support_layer_height",
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers", "support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern", "support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang", "support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance", "filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body", "sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width", "ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",

View file

@ -248,6 +248,13 @@ static t_config_enum_values s_keys_map_SeamPosition {
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition)
static t_config_enum_values s_keys_map_InternalBridgeFilter {
{ "disabled", ibfDisabled },
{ "limited", ibfLimited },
{ "nofilter", ibfNofilter },
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InternalBridgeFilter)
static const t_config_enum_values s_keys_map_SLADisplayOrientation = { static const t_config_enum_values s_keys_map_SLADisplayOrientation = {
{ "landscape", sladoLandscape}, { "landscape", sladoLandscape},
{ "portrait", sladoPortrait} { "portrait", sladoPortrait}
@ -1220,6 +1227,32 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true)); def->set_default_value(new ConfigOptionBool(true));
def = this->add("dont_filter_internal_bridges", coEnum);
def->label = L("Don't filter out small internal bridges (experimental)");
def->category = L("Quality");
def->tooltip = L("This option can help reducing pillowing on top surfaces in heavily slanted or curved models.\n\n"
"By default, small internal bridges are filtered out and the internal solid infill is printed directly"
" over the sparse infill. This works well in most cases, speeding up printing without too much compromise"
" on top surface quality. \n\nHowever, in heavily slanted or curved models especially where too low sparse"
" infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
"Enabling this option will print internal bridge layer over slightly unsupported internal"
" solid infill. The options below control the amount of filtering, i.e. the amount of internal bridges "
"created.\n\n"
"Disabled - Disables this option. This is the default behaviour and works well in most cases.\n\n"
"Limited filtering - Creates internal bridges on heavily slanted surfaces, while avoiding creating "
"uncessesary interal bridges. This works well for most difficult models.\n\n"
"No filtering - Creates internal bridges on every potential internal overhang. This option is useful "
"for heavily slanted top surface models. However, in most cases it creates too many unecessary bridges.");
def->enum_keys_map = &ConfigOptionEnum<InternalBridgeFilter>::get_enum_values();
def->enum_values.push_back("disabled");
def->enum_values.push_back("limited");
def->enum_values.push_back("nofilter");
def->enum_labels.push_back(L("Disabled"));
def->enum_labels.push_back(L("Limited filtering"));
def->enum_labels.push_back(L("No filtering"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<InternalBridgeFilter>(ibfDisabled));
def = this->add("max_bridge_length", coFloat); def = this->add("max_bridge_length", coFloat);
def->label = L("Max bridge length"); def->label = L("Max bridge length");

View file

@ -152,6 +152,10 @@ enum SeamPosition {
spNearest, spAligned, spRear, spRandom spNearest, spAligned, spRear, spRandom
}; };
enum InternalBridgeFilter {
ibfDisabled, ibfLimited, ibfNofilter
};
enum LiftType { enum LiftType {
NormalLift, NormalLift,
SpiralLift, SpiralLift,
@ -744,6 +748,7 @@ PRINT_CONFIG_CLASS_DEFINE(
// Orca internal thick bridge // Orca internal thick bridge
((ConfigOptionBool, thick_bridges)) ((ConfigOptionBool, thick_bridges))
((ConfigOptionBool, thick_internal_bridges)) ((ConfigOptionBool, thick_internal_bridges))
((ConfigOptionEnum<InternalBridgeFilter>, dont_filter_internal_bridges))
// Overhang angle threshold. // Overhang angle threshold.
((ConfigOptionInt, support_threshold_angle)) ((ConfigOptionInt, support_threshold_angle))
((ConfigOptionFloat, support_object_xy_distance)) ((ConfigOptionFloat, support_object_xy_distance))

View file

@ -1969,7 +1969,6 @@ template<typename T> void debug_draw(std::string name, const T& a, const T& b, c
void PrintObject::bridge_over_infill() void PrintObject::bridge_over_infill()
{ {
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info();
struct CandidateSurface struct CandidateSurface
{ {
CandidateSurface(const Surface *original_surface, CandidateSurface(const Surface *original_surface,
@ -1991,12 +1990,20 @@ void PrintObject::bridge_over_infill()
}; };
std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer; std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer;
// Orca:
// Detect use of lightning infill. Moved earlier in the function to pass to the gather and filter surfaces threads.
bool has_lightning_infill = false;
for (size_t i = 0; i < this->num_printing_regions(); i++) {
if (this->printing_region(i).config().sparse_infill_pattern == ipLightning) {
has_lightning_infill = true;
break;
}
}
// SECTION to gather and filter surfaces for expanding, and then cluster them by layer // SECTION to gather and filter surfaces for expanding, and then cluster them by layer
{ {
tbb::concurrent_vector<CandidateSurface> candidate_surfaces; tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), &candidate_surfaces, has_lightning_infill](tbb::blocked_range<size_t> r) {
&candidate_surfaces](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *layer = po->get_layer(lidx); const Layer *layer = po->get_layer(lidx);
@ -2019,18 +2026,33 @@ void PrintObject::bridge_over_infill()
} }
} }
unsupported_area = closing(unsupported_area, float(SCALED_EPSILON)); unsupported_area = closing(unsupported_area, float(SCALED_EPSILON));
// Orca:
// Lightning infill benefits from always having a bridge layer so don't filter out small unsupported areas. Also, don't filter small internal unsupported areas if the user has requested so.
double expansion_multiplier = 3;
if(has_lightning_infill || po->config().dont_filter_internal_bridges.value !=ibfDisabled){
expansion_multiplier = 1;
}
// By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids // By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
// NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole // NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything
lower_layer_solids = expand(lower_layer_solids, (1 + 3) * spacing); // then expand back (opening), and further for parts supported by internal solids lower_layer_solids = expand(lower_layer_solids, (1 + expansion_multiplier) * spacing); // then expand back (opening), and further for parts supported by internal solids
// By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters. // By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters.
unsupported_area = shrink(unsupported_area, 3 * spacing); unsupported_area = shrink(unsupported_area, expansion_multiplier * spacing);
unsupported_area = diff(unsupported_area, lower_layer_solids); unsupported_area = diff(unsupported_area, lower_layer_solids);
for (const LayerRegion *region : layer->regions()) { for (const LayerRegion *region : layer->regions()) {
SurfacesPtr region_internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid); SurfacesPtr region_internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid);
for (const Surface *s : region_internal_solids) { for (const Surface *s : region_internal_solids) {
Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area);
// Orca: If the user has selected to always support internal overhanging regions, no matter how small
// skip the filtering
if (po->config().dont_filter_internal_bridges.value == ibfNofilter){
// expand the unsupported area by 4x spacing to trigger internal bridging
unsupported = expand(unsupported, 4 * spacing);
candidate_surfaces.push_back(CandidateSurface(s, lidx, unsupported, region, 0));
}else{
// The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.
// These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs
bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
@ -2061,6 +2083,7 @@ void PrintObject::bridge_over_infill()
} }
} }
} }
}
}); });
for (const CandidateSurface &c : candidate_surfaces) { for (const CandidateSurface &c : candidate_surfaces) {
@ -2071,13 +2094,6 @@ void PrintObject::bridge_over_infill()
// LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the // LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the
// lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends. // lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends.
// It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure. // It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure.
bool has_lightning_infill = false;
for (size_t i = 0; i < this->num_printing_regions(); i++) {
if (this->printing_region(i).config().sparse_infill_pattern == ipLightning) {
has_lightning_infill = true;
break;
}
}
if (has_lightning_infill) { if (has_lightning_infill) {
// Prepare backup data for the Layer Region infills. Before modfiyng the layer region, we backup its fill surfaces by moving! them into this map. // Prepare backup data for the Layer Region infills. Before modfiyng the layer region, we backup its fill surfaces by moving! them into this map.
// then a copy is created, modifiyed and passed to lightning infill generator. After generator is created, we restore the original state of the fills // then a copy is created, modifiyed and passed to lightning infill generator. After generator is created, we restore the original state of the fills

View file

@ -1977,6 +1977,7 @@ void TabPrint::build()
optgroup->append_single_option_line("bridge_density"); optgroup->append_single_option_line("bridge_density");
optgroup->append_single_option_line("thick_bridges"); optgroup->append_single_option_line("thick_bridges");
optgroup->append_single_option_line("thick_internal_bridges"); optgroup->append_single_option_line("thick_internal_bridges");
optgroup->append_single_option_line("dont_filter_internal_bridges");
optgroup = page->new_optgroup(L("Overhangs"), L"param_advanced"); optgroup = page->new_optgroup(L("Overhangs"), L"param_advanced");
optgroup->append_single_option_line("detect_overhang_wall"); optgroup->append_single_option_line("detect_overhang_wall");