From e2169484a3ae45ba48383e88085710d5cca551a9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 Nov 2021 12:21:33 +0100 Subject: [PATCH] New parameter "support_material_min_diameter" to filter out support overhangs with enclosing circle diameter smaller than the new parameter. --- src/libslic3r/Geometry/Circle.hpp | 4 ++ src/libslic3r/Polygon.cpp | 69 +++++++++++++++++++++++++++ src/libslic3r/Polygon.hpp | 5 ++ src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 12 +++++ src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/SupportMaterial.cpp | 11 ++++- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 10 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index a8a993f8be..dc490cf8e0 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -141,6 +141,10 @@ Circle smallest_enclosing_circle_welzl(const Points &points, const typen } // Randomized algorithm by Emo Welzl. The returned circle radius is inflated by SCALED_EPSILON. +inline CircleSqd smallest_enclosing_circle2_welzl(const Points &points) +{ + return smallest_enclosing_circle2_welzl(points, SCALED_EPSILON); +} inline Circled smallest_enclosing_circle_welzl(const Points &points) { return smallest_enclosing_circle_welzl(points, SCALED_EPSILON); diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 42a6cc2095..87bc05b852 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -1,9 +1,13 @@ #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "Exception.hpp" +#include "Geometry/Circle.hpp" #include "Polygon.hpp" #include "Polyline.hpp" +#include +#include + namespace Slic3r { Lines Polygon::lines() const @@ -437,6 +441,8 @@ bool remove_degenerate(Polygons &polys) return modified; } +// Remove polygons with absolute area smaller then threshold. +// Thus this function may fill holes of some polygons, which may not be what one expects. bool remove_small(Polygons &polys, double min_area) { bool modified = false; @@ -454,6 +460,69 @@ bool remove_small(Polygons &polys, double min_area) return modified; } +// Remove polygons with minimum enclosing circle diameter smaller than min_diameter. +// Again, this function may fill holes of some polygons, which may not be what one expects. +void remove_with_small_diameter(Polygons &polygons, double min_diameter) +{ + coord_t min_size = coord_t(min_diameter); + double min_diameter2 = min_diameter * min_diameter; + std::mt19937 g; + Points poly_shuffled; + size_t end = 0; + for (size_t i = 0; i < polygons.size(); ++ i) { + Polygon &poly = polygons[i]; + bool keep = true; + if (poly.size() < 2) { + keep = false; + } else { + Point pmin = poly.front(); + Point pmax = poly.points[1]; + for (size_t k = 2; k < poly.size(); ++k) { + const Point& p = poly.points[k]; + pmin = pmin.cwiseMin(p); + pmax = pmax.cwiseMax(p); + if (pmax.x() - pmin.x() > min_size || pmax.y() - pmin.y() > min_size) { + // Diameter of the polygon is certainly above min_diameter. + keep = true; + break; + } + } + if (! keep) { + // Measure diameter of a circumscribed circle around the polygon's bounding box. + Vec2d size = pmax.cast() - pmin.cast(); + if (size.squaredNorm() > min_diameter2) { + // There is a chance that the diameter of a polygon is above min_diameter. + poly_shuffled = poly.points; + std::shuffle(poly_shuffled.begin(), poly_shuffled.end(), g); + if (Geometry::CircleSqd circle = Geometry::smallest_enclosing_circle2_welzl(poly_shuffled); circle.radius2 > 0.25 * min_diameter2) + keep = true; + } + } + } +#if 0 + //FIXME implement smarter removal of holes: Holes shall not be removed if their outer contour was not removed. + if (!keep) { + if (poly.is_counter_clockwise()) { + // Remove the outer contour. + if (i < --end) { + std::swap(poly, polygons[end]) + --i; + } + } + else + holes.emplace_back(std::move(poly)); + } +#else + if (keep) { + if (end < i) + polygons[end] = std::move(poly); + ++ end; + } +#endif + polygons.erase(polygons.begin() + end, polygons.end()); + } +} + void remove_collinear(Polygon &poly) { if (poly.points.size() > 2) { diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 29800a31d4..cac858a4e9 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -112,7 +112,12 @@ bool remove_sticks(Polygons &polys); // Remove polygons with less than 3 edges. bool remove_degenerate(Polygons &polys); +// Remove polygons with absolute area smaller then threshold. +// Thus this function may fill holes of some polygons, which may not be what one expects. bool remove_small(Polygons &polys, double min_area); +// Remove polygons with minimum enclosing circle diameter smaller than min_diameter. +// Again, this function may fill holes of some polygons, which may not be what one expects. +void remove_with_small_diameter(Polygons& polygons, double min_diameter); void remove_collinear(Polygon &poly); void remove_collinear(Polygons &polys); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 09cd384687..546154a1a8 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -434,7 +434,7 @@ static std::vector s_Preset_print_options { "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", - "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", + "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_min_diameter", "support_material_contact_distance", "support_material_bottom_contact_distance", "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 232e724e76..475795eae4 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2670,6 +2670,18 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(smpRectilinear)); + def = this->add("support_material_min_diameter", coFloat); + def->label = L("Minimum support size"); + def->category = L("Support material"); + def->tooltip = L("Minimum diameter of an overhang to be supported. " + "Increasing this value will filter out likely unneccessary supports of tiny overhangs protruding from an object body. " + "However filtering small overhangs may leave some long thin features unsupported, " + "requiring user intervention using the paint-on supports tool."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + def = this->add("support_material_spacing", coFloat); def->label = L("Pattern spacing"); def->category = L("Support material"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d7409d12ce..7979387976 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -492,6 +492,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, support_material_interface_spacing)) ((ConfigOptionFloatOrPercent, support_material_interface_speed)) ((ConfigOptionEnum, support_material_pattern)) + ((ConfigOptionFloat, support_material_min_diameter)) ((ConfigOptionEnum, support_material_interface_pattern)) // Morphological closing of support areas. Only used for "sung" supports. ((ConfigOptionFloat, support_material_closing_radius)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index eee0c2abab..c932c9335c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -554,6 +554,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_material_interface_contact_loops" || opt_key == "support_material_interface_extruder" || opt_key == "support_material_interface_spacing" + || opt_key == "support_material_min_diameter" || opt_key == "support_material_pattern" || opt_key == "support_material_style" || opt_key == "support_material_xy_spacing" diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 7ef289757d..bc867b748f 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1446,7 +1446,9 @@ static inline std::tuple detect_overhangs( const PrintObjectConfig &object_config, SupportAnnotations &annotations, SlicesMarginCache &slices_margin, - const double gap_xy + const double gap_xy, + // Scaled, minimum diameter of a support island to be supported. + const double min_support_diameter #ifdef SLIC3R_DEBUG , size_t iRun #endif // SLIC3R_DEBUG @@ -1601,6 +1603,10 @@ static inline std::tuple detect_overhangs( SupportMaterialInternal::remove_bridges_from_contacts( print_config, lower_layer, lower_layer_polygons, *layerm, fw, diff_polygons); + if (min_support_diameter > 0) + // Remove polygons with their circumscribed circle radius smaller than min_diameter. + remove_with_small_diameter(diff_polygons, min_support_diameter); + if (diff_polygons.empty()) continue; @@ -1995,6 +2001,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), [this, &object, &annotations, &layer_storage, &layer_storage_mutex, &contact_out] (const tbb::blocked_range& range) { + const double min_support_diameter = scaled(object.config().support_material_min_diameter.value); for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { const Layer &layer = *object.layers()[layer_id]; @@ -2002,7 +2009,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ SlicesMarginCache slices_margin; auto [overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset] = - detect_overhangs(layer, layer_id, lower_layer_polygons, *m_print_config, *m_object_config, annotations, slices_margin, m_support_params.gap_xy + detect_overhangs(layer, layer_id, lower_layer_polygons, *m_print_config, *m_object_config, annotations, slices_margin, m_support_params.gap_xy, min_support_diameter #ifdef SLIC3R_DEBUG , iRun #endif // SLIC3R_DEBUG diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 0b2af37b8e..73ab7383aa 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -282,7 +282,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_support_soluble = have_support_material && config->opt_float("support_material_contact_distance") == 0; auto support_material_style = config->opt_enum("support_material_style"); for (auto el : { "support_material_style", "support_material_pattern", "support_material_with_sheath", - "support_material_spacing", "support_material_angle", + "support_material_spacing", "support_material_angle", "support_material_min_diameter", "support_material_interface_pattern", "support_material_interface_layers", "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", "support_material_xy_spacing" }) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1a96f4bafe..29ea44e584 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1535,6 +1535,7 @@ void TabPrint::build() optgroup->append_single_option_line("support_material", category_path + "generate-support-material"); optgroup->append_single_option_line("support_material_auto", category_path + "auto-generated-supports"); optgroup->append_single_option_line("support_material_threshold", category_path + "overhang-threshold"); + optgroup->append_single_option_line("support_material_min_diameter", category_path + "support-min-diameter"); optgroup->append_single_option_line("support_material_enforce_layers", category_path + "enforce-support-for-the-first"); optgroup->append_single_option_line("raft_first_layer_density", category_path + "raft-first-layer-density"); optgroup->append_single_option_line("raft_first_layer_expansion", category_path + "raft-first-layer-expansion");