mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 15:07:31 -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",
|
||||
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
|
||||
"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",
|
||||
"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",
|
||||
|
|
|
@ -248,6 +248,13 @@ static t_config_enum_values s_keys_map_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 = {
|
||||
{ "landscape", sladoLandscape},
|
||||
{ "portrait", sladoPortrait}
|
||||
|
@ -1220,6 +1227,32 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
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->label = L("Max bridge length");
|
||||
|
|
|
@ -152,6 +152,10 @@ enum SeamPosition {
|
|||
spNearest, spAligned, spRear, spRandom
|
||||
};
|
||||
|
||||
enum InternalBridgeFilter {
|
||||
ibfDisabled, ibfLimited, ibfNofilter
|
||||
};
|
||||
|
||||
enum LiftType {
|
||||
NormalLift,
|
||||
SpiralLift,
|
||||
|
@ -744,6 +748,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
// Orca internal thick bridge
|
||||
((ConfigOptionBool, thick_bridges))
|
||||
((ConfigOptionBool, thick_internal_bridges))
|
||||
((ConfigOptionEnum<InternalBridgeFilter>, dont_filter_internal_bridges))
|
||||
// Overhang angle threshold.
|
||||
((ConfigOptionInt, support_threshold_angle))
|
||||
((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()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info();
|
||||
|
||||
struct CandidateSurface
|
||||
{
|
||||
CandidateSurface(const Surface *original_surface,
|
||||
|
@ -1991,12 +1990,20 @@ void PrintObject::bridge_over_infill()
|
|||
};
|
||||
|
||||
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
|
||||
{
|
||||
tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this),
|
||||
&candidate_surfaces](tbb::blocked_range<size_t> r) {
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
|
||||
const Layer *layer = po->get_layer(lidx);
|
||||
|
@ -2019,44 +2026,60 @@ void PrintObject::bridge_over_infill()
|
|||
}
|
||||
}
|
||||
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
|
||||
// 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 = 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.
|
||||
unsupported_area = shrink(unsupported_area, 3 * spacing);
|
||||
unsupported_area = shrink(unsupported_area, expansion_multiplier * spacing);
|
||||
unsupported_area = diff(unsupported_area, lower_layer_solids);
|
||||
|
||||
for (const LayerRegion *region : layer->regions()) {
|
||||
SurfacesPtr region_internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid);
|
||||
for (const Surface *s : region_internal_solids) {
|
||||
Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area);
|
||||
// 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
|
||||
bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
|
||||
if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
|
||||
Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
|
||||
// after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
|
||||
for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
|
||||
double area = p.area();
|
||||
if (area < spacing * scale_(12.0) && area > spacing * spacing) {
|
||||
worth_bridging.push_back(p);
|
||||
|
||||
// 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.
|
||||
// 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;
|
||||
if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
|
||||
Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
|
||||
// after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
|
||||
for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
|
||||
double area = p.area();
|
||||
if (area < spacing * scale_(12.0) && area > spacing * spacing) {
|
||||
worth_bridging.push_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
worth_bridging = intersection(closing(worth_bridging, float(SCALED_EPSILON)), s->expolygon);
|
||||
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
|
||||
worth_bridging = intersection(closing(worth_bridging, float(SCALED_EPSILON)), s->expolygon);
|
||||
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
|
||||
to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging),
|
||||
to_lines(unsupported_area));
|
||||
debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
|
||||
to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging),
|
||||
to_lines(unsupported_area));
|
||||
#endif
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
|
||||
to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))),
|
||||
to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
|
||||
to_lines(unsupported_area));
|
||||
debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
|
||||
to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))),
|
||||
to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
|
||||
to_lines(unsupported_area));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 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.
|
||||
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) {
|
||||
// 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
|
||||
|
|
|
@ -1977,6 +1977,7 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("bridge_density");
|
||||
optgroup->append_single_option_line("thick_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->append_single_option_line("detect_overhang_wall");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue