From f1977b07be304076be720b058757bc4d36158e83 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 9 Feb 2023 16:47:57 +0100 Subject: [PATCH] Organic supports: Adding bridge detection using the same algorithms as the regular supports. Partial fix to #9493 --- src/libslic3r/ClipperUtils.hpp | 2 + src/libslic3r/SupportMaterial.cpp | 158 +++++++++++++++--------------- src/libslic3r/SupportMaterial.hpp | 9 ++ src/libslic3r/TreeSupport.cpp | 9 +- 4 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 65ecfb76a1..8f77e6da8b 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -361,6 +361,8 @@ inline Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float del { assert(delta > 0); return offset(polygon, delta, joinType, miterLimit); } inline Slic3r::Polygons expand(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit) { assert(delta > 0); return offset(polygons, delta, joinType, miterLimit); } +inline Slic3r::Polygons expand(const Slic3r::ExPolygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit) + { assert(delta > 0); return offset(polygons, delta, joinType, miterLimit); } inline Slic3r::ExPolygons expand_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit) { assert(delta > 0); return offset_ex(polygons, delta, joinType, miterLimit); } // Input polygons for shrinking shall be "normalized": There must be no overlap / intersections between the input polygons. diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 11d34cafdc..836dc8ac97 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1260,87 +1260,86 @@ namespace SupportMaterialInternal { collect_bridging_perimeter_areas(*static_cast(ee), expansion_scaled, out); } } +} - static void remove_bridges_from_contacts( - const PrintConfig &print_config, - const Layer &lower_layer, - const Polygons &lower_layer_polygons, - const LayerRegion &layerm, - float fw, - Polygons &contact_polygons) +void remove_bridges_from_contacts( + const PrintConfig &print_config, + const Layer &lower_layer, + const LayerRegion &layerm, + float fw, + Polygons &contact_polygons) +{ + // compute the area of bridging perimeters + Polygons bridges; { - // compute the area of bridging perimeters - Polygons bridges; - { - // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. - Polygons lower_grown_slices = expand(lower_layer_polygons, - //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. - 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm.region().config().perimeter_extruder-1))), - SUPPORT_SURFACES_OFFSET_PARAMETERS); - // Collect perimeters of this layer. - //FIXME split_at_first_point() could split a bridge mid-way - #if 0 - Polylines overhang_perimeters = layerm.perimeters.as_polylines(); - // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() - for (Polyline &polyline : overhang_perimeters) - polyline.points[0].x += 1; - // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. - overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); - #else - Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); - #endif - - // only consider straight overhangs - // only consider overhangs having endpoints inside layer's slices - // convert bridging polylines into polygons by inflating them with their thickness - // since we're dealing with bridges, we can't assume width is larger than spacing, - // so we take the largest value and also apply safety offset to be ensure no gaps - // are left in between - Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter); - //FIXME one may want to use a maximum of bridging flow width and normal flow width, as the perimeters are calculated using the normal flow - // and then turned to bridging flow, thus their centerlines are derived from non-bridging flow and expanding them by a bridging flow - // may not expand them to the edge of their respective islands. - const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); - for (Polyline &polyline : overhang_perimeters) - if (polyline.is_straight()) { - // This is a bridge - polyline.extend_start(fw); - polyline.extend_end(fw); - // Is the straight perimeter segment supported at both sides? - Point pts[2] = { polyline.first_point(), polyline.last_point() }; - bool supported[2] = { false, false }; - for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i) - for (int j = 0; j < 2; ++ j) - if (! supported[j] && lower_layer.lslices_ex[i].bbox.contains(pts[j]) && lower_layer.lslices[i].contains(pts[j])) - supported[j] = true; - if (supported[0] && supported[1]) - // Offset a polyline into a thick line. - polygons_append(bridges, offset(polyline, w)); - } - bridges = union_(bridges); - } - // remove the entire bridges and only support the unsupported edges - //FIXME the brided regions are already collected as layerm.bridged. Use it? - for (const Surface &surface : layerm.fill_surfaces()) - if (surface.surface_type == stBottomBridge && surface.bridge_angle >= 0.0) - polygons_append(bridges, surface.expolygon); - //FIXME add the gap filled areas. Extrude the gaps with a bridge flow? - // Remove the unsupported ends of the bridges from the bridged areas. - //FIXME add supports at regular intervals to support long bridges! - bridges = diff(bridges, - // Offset unsupported edges into polygons. - offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); - // Remove bridged areas from the supported areas. - contact_polygons = diff(contact_polygons, bridges, ApplySafetyOffset::Yes); - - #ifdef SLIC3R_DEBUG - static int iRun = 0; - SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++), - { { { union_ex(offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } }, - { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(bridges) }, { "bridges", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ + // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. + Polygons lower_grown_slices = expand(lower_layer.lslices, + //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. + 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm.region().config().perimeter_extruder-1))), + SUPPORT_SURFACES_OFFSET_PARAMETERS); + // Collect perimeters of this layer. + //FIXME split_at_first_point() could split a bridge mid-way + #if 0 + Polylines overhang_perimeters = layerm.perimeters.as_polylines(); + // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() + for (Polyline &polyline : overhang_perimeters) + polyline.points[0].x += 1; + // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); + #else + Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); + #endif + + // only consider straight overhangs + // only consider overhangs having endpoints inside layer's slices + // convert bridging polylines into polygons by inflating them with their thickness + // since we're dealing with bridges, we can't assume width is larger than spacing, + // so we take the largest value and also apply safety offset to be ensure no gaps + // are left in between + Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter); + //FIXME one may want to use a maximum of bridging flow width and normal flow width, as the perimeters are calculated using the normal flow + // and then turned to bridging flow, thus their centerlines are derived from non-bridging flow and expanding them by a bridging flow + // may not expand them to the edge of their respective islands. + const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); + for (Polyline &polyline : overhang_perimeters) + if (polyline.is_straight()) { + // This is a bridge + polyline.extend_start(fw); + polyline.extend_end(fw); + // Is the straight perimeter segment supported at both sides? + Point pts[2] = { polyline.first_point(), polyline.last_point() }; + bool supported[2] = { false, false }; + for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i) + for (int j = 0; j < 2; ++ j) + if (! supported[j] && lower_layer.lslices_ex[i].bbox.contains(pts[j]) && lower_layer.lslices[i].contains(pts[j])) + supported[j] = true; + if (supported[0] && supported[1]) + // Offset a polyline into a thick line. + polygons_append(bridges, offset(polyline, w)); + } + bridges = union_(bridges); } + // remove the entire bridges and only support the unsupported edges + //FIXME the brided regions are already collected as layerm.bridged. Use it? + for (const Surface &surface : layerm.fill_surfaces()) + if (surface.surface_type == stBottomBridge && surface.bridge_angle >= 0.0) + polygons_append(bridges, surface.expolygon); + //FIXME add the gap filled areas. Extrude the gaps with a bridge flow? + // Remove the unsupported ends of the bridges from the bridged areas. + //FIXME add supports at regular intervals to support long bridges! + bridges = diff(bridges, + // Offset unsupported edges into polygons. + offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + // Remove bridged areas from the supported areas. + contact_polygons = diff(contact_polygons, bridges, ApplySafetyOffset::Yes); + + #ifdef SLIC3R_DEBUG + static int iRun = 0; + SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++), + { { { union_ex(offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } }, + { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(bridges) }, { "bridges", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ } std::vector PrintObjectSupportMaterial::buildplate_covered(const PrintObject &object) const @@ -1558,8 +1557,7 @@ static inline std::tuple detect_overhangs( if (object_config.dont_support_bridges) //FIXME Expensive, potentially not precise enough. Misses gap fill extrusions, which bridge. - SupportMaterialInternal::remove_bridges_from_contacts( - print_config, lower_layer, lower_layer_polygons, *layerm, fw, diff_polygons); + remove_bridges_from_contacts(print_config, lower_layer, *layerm, fw, diff_polygons); if (diff_polygons.empty()) continue; diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index b578adb306..fc5588d82c 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -147,6 +147,15 @@ struct SupportParameters { bool with_sheath; }; +// Remove bridges from support contact areas. +// To be called if PrintObjectConfig::dont_support_bridges. +void remove_bridges_from_contacts( + const PrintConfig &print_config, + const Layer &lower_layer, + const LayerRegion &layerm, + float fw, + Polygons &contact_polygons); + // Generate raft layers, also expand the 1st support layer // in case there is no raft layer to improve support adhesion. SupportGeneratorLayersPtr generate_raft_base( diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 8082fab8e4..00eec7df0c 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -226,6 +226,7 @@ void tree_supports_show_error(std::string_view message, bool critical) { std::vector out(print_object.layer_count(), Polygons{}); + const PrintConfig &print_config = print_object.print()->config(); const PrintObjectConfig &config = print_object.config(); const bool support_auto = config.support_material.value && config.support_material_auto.value; const int support_enforce_layers = config.support_material_enforce_layers.value; @@ -242,7 +243,8 @@ void tree_supports_show_error(std::string_view message, bool critical) size_t num_overhang_layers = support_auto ? out.size() : std::max(size_t(support_enforce_layers), enforcers_layers.size()); tbb::parallel_for(tbb::blocked_range(1, num_overhang_layers), - [&print_object, &enforcers_layers, &blockers_layers, support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, &throw_on_cancel, &out] + [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, + support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, &throw_on_cancel, &out] (const tbb::blocked_range &range) { for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { const Layer ¤t_layer = *print_object.get_layer(layer_id); @@ -275,6 +277,11 @@ void tree_supports_show_error(std::string_view message, bool critical) } if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty())) overhangs = diff(overhangs, blockers_layers[layer_id], ApplySafetyOffset::Yes); + if (config.dont_support_bridges) { + for (const LayerRegion *layerm : current_layer.regions()) + remove_bridges_from_contacts(print_config, lower_layer, *layerm, + float(layerm->flow(frExternalPerimeter).scaled_width()), overhangs); + } } //check_self_intersections(overhangs, "generate_overhangs1"); if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) {