mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 23:17:35 -06:00
[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:
parent
b4925363d6
commit
f7b92d9813
5 changed files with 93 additions and 38 deletions
|
@ -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",
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue