From f7b92d981386d1937f147b15d856305470844139 Mon Sep 17 00:00:00 2001 From: Ioannis Giannakas <59056762+igiannakas@users.noreply.github.com> Date: Sat, 13 Jan 2024 17:20:08 +0200 Subject: [PATCH 1/3] [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 --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 33 +++++++++++++ src/libslic3r/PrintConfig.hpp | 5 ++ src/libslic3r/PrintObject.cpp | 90 +++++++++++++++++++++-------------- src/slic3r/GUI/Tab.cpp | 1 + 5 files changed, 93 insertions(+), 38 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index e638a96570..84ef8d7522 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -786,7 +786,7 @@ static std::vector 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", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 5efec684ef..6d10350b2b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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::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(ibfDisabled)); + def = this->add("max_bridge_length", coFloat); def->label = L("Max bridge length"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index aa1c21ce4c..94a5efccc8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -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, dont_filter_internal_bridges)) // Overhang angle threshold. ((ConfigOptionInt, support_threshold_angle)) ((ConfigOptionFloat, support_object_xy_distance)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 9af5df80b6..da0aff7963 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1969,7 +1969,6 @@ template 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> 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 candidate_surfaces; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), - &candidate_surfaces](tbb::blocked_range r) { + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), &candidate_surfaces, has_lightning_infill](tbb::blocked_range 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)); + +#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)); +#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)); +#endif } - 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)); -#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)); -#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 diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1eb72b26c6..f5b644d588 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -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"); From d26513e635bad28a630f698fd3a8371544ba0b12 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Mon, 8 Jan 2024 23:33:56 +0800 Subject: [PATCH 2/3] Pressure advance Line: support different nozzle sizes --- src/libslic3r/GCode.cpp | 8 +++++--- src/libslic3r/calib.cpp | 18 +++++++++++++----- src/libslic3r/calib.hpp | 11 ++++++----- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 2c28e4841f..a9336b5f4f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2463,9 +2463,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato CalibPressureAdvanceLine pa_test(this); - auto fast_speed = CalibPressureAdvance::find_optimal_PA_speed(print.full_print_config(), pa_test.line_width(), 0.2); - auto slow_speed = std::max(20.0, fast_speed / 10.0); - + auto fast_speed = CalibPressureAdvance::find_optimal_PA_speed(print.full_print_config(), pa_test.line_width(), pa_test.height_layer()); + auto slow_speed = std::max(10.0, fast_speed / 10.0); + if (fast_speed < slow_speed + 5) + fast_speed = slow_speed + 5; + pa_test.set_speed(fast_speed, slow_speed); pa_test.draw_numbers() = print.calib_params().print_numbers; gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)) + 1); diff --git a/src/libslic3r/calib.cpp b/src/libslic3r/calib.cpp index 162d7b9b5a..224479c097 100644 --- a/src/libslic3r/calib.cpp +++ b/src/libslic3r/calib.cpp @@ -424,6 +424,13 @@ std::string CalibPressureAdvance::draw_box(GCodeWriter &writer, double min_x, do return gcode.str(); } +CalibPressureAdvanceLine::CalibPressureAdvanceLine(GCode* gcodegen) + : CalibPressureAdvance(gcodegen->config()), mp_gcodegen(gcodegen), m_nozzle_diameter(gcodegen->config().nozzle_diameter.get_at(0)) +{ + m_line_width = m_nozzle_diameter < 0.51 ? m_nozzle_diameter * 1.5 : m_nozzle_diameter * 1.05; + m_height_layer = gcodegen->config().initial_layer_print_height; + m_number_line_width = m_thin_line_width = m_nozzle_diameter; +}; std::string CalibPressureAdvanceLine::generate_test(double start_pa /*= 0*/, double step_pa /*= 0.002*/, int count /*= 10*/) { @@ -496,14 +503,15 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star // gcode << move_to(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 7), writer); // gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 2), thin_e_per_mm * 7); - DrawBoxOptArgs default_box_opt_args(2, m_height_layer, 0.6, fast); + const auto box_start_x = start_x + m_length_short + m_length_long + m_length_short; + DrawBoxOptArgs default_box_opt_args(2, m_height_layer, m_line_width, fast); default_box_opt_args.is_filled = true; - gcode << draw_box(writer, start_x + m_length_short + m_length_long + m_length_short, start_y - m_space_y, number_spacing() * 8, - (num + 1) * m_space_y, default_box_opt_args); + gcode << draw_box(writer, box_start_x, start_y - m_space_y, + number_spacing() * 8, (num + 1) * m_space_y, default_box_opt_args); gcode << writer.travel_to_z(m_height_layer*2); for (int i = 0; i < num; i += 2) { - gcode << draw_number(start_x + m_length_short + m_length_long + m_length_short + 3, y_pos + i * m_space_y + m_space_y / 2, - start_pa + i * step_pa, m_draw_digit_mode, m_number_line_width, number_e_per_mm, 3600, writer); + gcode << draw_number(box_start_x + 3 + m_line_width, y_pos + i * m_space_y + m_space_y / 2, start_pa + i * step_pa, m_draw_digit_mode, + m_number_line_width, number_e_per_mm, 3600, writer); } } return gcode.str(); diff --git a/src/libslic3r/calib.hpp b/src/libslic3r/calib.hpp index fdabd47cc1..ee74d98a63 100644 --- a/src/libslic3r/calib.hpp +++ b/src/libslic3r/calib.hpp @@ -187,7 +187,7 @@ protected: class CalibPressureAdvanceLine : public CalibPressureAdvance { public: - CalibPressureAdvanceLine(GCode *gcodegen) : CalibPressureAdvance(gcodegen->config()), mp_gcodegen(gcodegen),m_nozzle_diameter(gcodegen->config().nozzle_diameter.get_at(0)){}; + CalibPressureAdvanceLine(GCode* gcodegen); ~CalibPressureAdvanceLine(){}; std::string generate_test(double start_pa = 0, double step_pa = 0.002, int count = 50); @@ -199,6 +199,7 @@ public: } const double &line_width() { return m_line_width; }; + const double &height_layer() { return m_height_layer; }; bool is_delta() const; bool &draw_numbers() { return m_draw_numbers; } @@ -212,10 +213,10 @@ private: double m_nozzle_diameter; double m_slow_speed, m_fast_speed; - const double m_height_layer{0.2}; - const double m_line_width{0.6}; - const double m_thin_line_width{0.44}; - const double m_number_line_width{0.48}; + double m_height_layer{0.2}; + double m_line_width{0.6}; + double m_thin_line_width{0.44}; + double m_number_line_width{0.48}; const double m_space_y{3.5}; double m_length_short{20.0}, m_length_long{40.0}; From b7b22eb78f1193a691ee47f11b60bbcd3aaefdf4 Mon Sep 17 00:00:00 2001 From: Tuomas Salokanto Date: Sun, 14 Jan 2024 10:18:30 +0200 Subject: [PATCH 3/3] Support custom IP camera (#3415) * Support custom IP camera Allow adding custom IP camera source while keeping possible built-in bambulab camera also functional and add button to switch between them. This uses WebView widget to show the stream. Upon loading the page, javascript is used to remove native controls and scroll- bars for aesthetic reasons. * Add partial support for PiP video HTMLVideoElement supports picture-in-picture video but the dedicated control is hidden in this implementation to have more integrated look in OrcaSlicer. Add right-click listener to the camera switch icon that opens the video element in a PiP window. Only works when the video is in