diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 1c8b4ad..905fcac 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -113,6 +113,47 @@ void Polyline::simplify(double tolerance) this->points = MultiPoint::douglas_peucker(this->points, tolerance); } +//w28 +Polylines Polyline::equally_spaced_lines(double distance) const +{ + Polylines lines; + Polyline line; + line.append(this->first_point()); + double len = 0; + + for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { + Vec2d p1 = line.points.back().cast(); + Vec2d v = it->cast() - p1; + double segment_length = v.norm(); + len += segment_length; + if (len < distance) + continue; + if (len == distance) { + line.append(*it); + lines.emplace_back(line); + + line.clear(); + line.append(*it); + len = 0; + continue; + } + double take = distance; + line.append((p1 + v * (take / v.norm())).cast()); + lines.emplace_back(line); + + line.clear(); + line.append(lines.back().last_point()); + --it; + len = -take; + } + if (line.size() == 1) { + line.append(this->last_point()); + if (line.first_point() != line.last_point()) + lines.emplace_back(line); + } + return lines; +} + #if 0 // This method simplifies all *lines* contained in the supplied area template diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 5c9a25f..3d783ff 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -81,6 +81,8 @@ public: using iterator = Points::iterator; using const_iterator = Points::const_iterator; + //w28 + Polylines equally_spaced_lines(double distance) const; }; inline bool operator==(const Polyline &lhs, const Polyline &rhs) { return lhs.points == rhs.points; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 6b62fab..1e5ce3a 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -489,6 +489,8 @@ static std::vector s_Preset_print_options { ,"elefant_foot_compensation_layers" //w27 ,"precise_z_height" + //w28 + ,"max_bridge_length" }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e513452..ac044d5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -868,6 +868,17 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); + //w28 + def = this->add("max_bridge_length", coFloat); + def->label = L("Max bridge length"); + def->category = L("Support material"); + def->tooltip = L("Max length of bridges that don't need support. Set it to 0 if you want all bridges to be supported, and set it to a " + "very large value if you don't want any bridges to be supported."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(10)); + def = this->add("duplicate_distance", coFloat); def->label = L("Distance between copies"); def->tooltip = L("Distance used for the auto-arrange feature of the plater."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index a1fccee..fe73c86 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -512,6 +512,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionEnum, brim_type)) ((ConfigOptionFloat, brim_width)) ((ConfigOptionBool, dont_support_bridges)) + //w28 + ((ConfigOptionFloat, max_bridge_length)) ((ConfigOptionFloat, elefant_foot_compensation)) //w26 ((ConfigOptionInt, elefant_foot_compensation_layers)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 05ecb33..57a5940 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -792,7 +792,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "raft_first_layer_density" || opt_key == "raft_first_layer_expansion" || opt_key == "dont_support_bridges" - || opt_key == "first_layer_extrusion_width") { + || opt_key == "first_layer_extrusion_width" + //w28 + || opt_key == "max_bridge_length") { steps.emplace_back(posSupportMaterial); } else if (opt_key == "bottom_solid_layers") { steps.emplace_back(posPrepareInfill); diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 4fb4ce9..d82b79a 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -120,6 +120,104 @@ void remove_bridges_from_contacts( #endif /* SLIC3R_DEBUG */ } +//w28 +void remove_bridges_from_contacts_select_area( + const PrintConfig &print_config, const Layer &lower_layer, const LayerRegion &layerm, float fw, Polygons &contact_polygons,const double max_bridge_length) +{ + Polygons bridges; + { + 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); +#if 0 + Polylines overhang_perimeters = layerm.perimeters.as_polylines(); + for (Polyline &polyline : overhang_perimeters) + polyline.points[0].x += 1; + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); +#else + Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); +#endif + Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter); + const float w = float(std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) ; + for (Polyline &polyline : overhang_perimeters) + if (polyline.is_straight()) { + polyline.extend_start(fw); + polyline.extend_end(fw); + 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]) { + Polylines lines; + if (polyline.length() > max_bridge_length + 10) { + // equally divide the polyline + float len = polyline.length() / ceil(polyline.length() / max_bridge_length); + lines = polyline.equally_spaced_lines(len); + for (auto &line : lines) { + line.clip_start(fw); + line.clip_end(fw); + } + } else + lines.push_back(polyline); + polygons_append(bridges, offset(lines, 0.5f * w + 10.f)); + } + } + bridges = union_(bridges); + + for (const Surface &surface : layerm.fill_surfaces().surfaces) + if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) { + auto bbox = get_extents(surface.expolygon); + auto bbox_size = bbox.size(); + if (bbox_size[0] < max_bridge_length && bbox_size[1] < max_bridge_length) + polygons_append(bridges, surface.expolygon); + else { + Polygons holes; + coord_t x0 = bbox.min.x(); + coord_t x1 = bbox.max.x(); + coord_t y0 = bbox.min.y(); + coord_t y1 = bbox.max.y(); + const int grid_lw = int(w ); + Vec2f bridge_direction{cos(surface.bridge_angle), sin(surface.bridge_angle)}; + if (fabs(bridge_direction(0)) > + fabs(bridge_direction(1))) { + int step = bbox_size(0) / ceil(bbox_size(0) / max_bridge_length); + for (int x = x0 + step; x < x1; x += step) { + Polygon poly; + poly.points = {Point(x - grid_lw, y0), Point(x + grid_lw, y0), Point(x + grid_lw, y1), Point(x - grid_lw, y1)}; + holes.emplace_back(poly); + } + } else { + int step = bbox_size(1) / ceil(bbox_size(1) / max_bridge_length); + for (int y = y0 + step; y < y1; y += step) { + Polygon poly; + poly.points = {Point(x0, y - grid_lw), Point(x0, y + grid_lw), Point(x1, y + grid_lw), Point(x1, y - grid_lw)}; + holes.emplace_back(poly); + } + } + auto expoly = diff_ex(surface.expolygon, holes); + polygons_append(bridges, expoly); + } + } + } + bridges = diff(bridges, + offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + 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 */ +} + // Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers. std::pair generate_interface_layers( const PrintObjectConfig &config, diff --git a/src/libslic3r/Support/SupportCommon.hpp b/src/libslic3r/Support/SupportCommon.hpp index 753665e..16e8102 100644 --- a/src/libslic3r/Support/SupportCommon.hpp +++ b/src/libslic3r/Support/SupportCommon.hpp @@ -22,6 +22,14 @@ void remove_bridges_from_contacts( const LayerRegion &layerm, float fw, Polygons &contact_polygons); +//w28 +void remove_bridges_from_contacts_select_area( + const PrintConfig &print_config, + const Layer &lower_layer, + const LayerRegion &layerm, + float fw, + Polygons &contact_polygons, + const double max_bridge_length = 0); // Turn some of the base layers into base interface layers. // For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index d91d492..b4ddbd2 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1143,6 +1143,9 @@ static inline std::tuple detect_overhangs( M_PI * double(object_config.support_material_threshold.value + 1) / 180. : // +1 makes the threshold inclusive 0.; float no_interface_offset = 0.f; + //w28 + double max_bridge_length = scale_(object_config.max_bridge_length.value); + bool bridge_break = object_config.max_bridge_length.value > 0; if (layer_id == 0) { @@ -1275,7 +1278,9 @@ static inline std::tuple detect_overhangs( if (object_config.dont_support_bridges) //FIXME Expensive, potentially not precise enough. Misses gap fill extrusions, which bridge. remove_bridges_from_contacts(print_config, lower_layer, *layerm, fw, diff_polygons); - + //w28 + if (!object_config.dont_support_bridges && bridge_break) + remove_bridges_from_contacts_select_area(print_config, lower_layer, *layerm, fw, diff_polygons, max_bridge_length); if (diff_polygons.empty()) continue; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 6c28647..65049c6 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -203,11 +203,15 @@ static std::vector>> group_me double tan_threshold = support_threshold_auto ? 0. : tan(M_PI * double(support_threshold + 1) / 180.); //FIXME this is a fudge constant! auto enforcer_overhang_offset = scaled(config.support_tree_tip_diameter.value); + //w28 + double max_bridge_length = scale_(config.max_bridge_length.value); + bool bridge_break = config.max_bridge_length.value > 0; + //w28 size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size())); tbb::parallel_for(tbb::blocked_range(1, num_overhang_layers), [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, - support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &throw_on_cancel, &out] + support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &throw_on_cancel, &out,bridge_break,max_bridge_length] (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); @@ -244,6 +248,12 @@ static std::vector>> group_me for (const LayerRegion *layerm : current_layer.regions()) remove_bridges_from_contacts(print_config, lower_layer, *layerm, float(layerm->flow(frExternalPerimeter).scaled_width()), overhangs); + } + //w28 + else if (!config.dont_support_bridges && bridge_break) { + for (const LayerRegion *layerm : current_layer.regions()) + remove_bridges_from_contacts_select_area(print_config, lower_layer, *layerm, float(scale_(config.extrusion_width)), + overhangs, max_bridge_length); } } //check_self_intersections(overhangs, "generate_overhangs1"); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 870c71e..85ef223 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -316,6 +316,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("support_material_threshold", have_support_material_auto); toggle_field("support_material_bottom_contact_distance", have_support_material && ! have_support_soluble); toggle_field("support_material_closing_radius", have_support_material && support_material_style == smsSnug); + //w28 + bool can_remove_bridge = have_support_material && !config->opt_bool("dont_support_bridges"); + toggle_field("max_bridge_length", can_remove_bridge); const bool has_organic_supports = support_material_style == smsOrganic && (config->opt_bool("support_material") || diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f7dfb22..1ee45b7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1562,6 +1562,8 @@ void TabPrint::build() optgroup->append_single_option_line("support_material_xy_spacing", category_path + "xy-separation-between-an-object-and-its-support"); optgroup->append_single_option_line("dont_support_bridges", category_path + "dont-support-bridges"); optgroup->append_single_option_line("support_material_synchronize_layers", category_path + "synchronize-with-object-layers"); + //w28 + optgroup->append_single_option_line("max_bridge_length", category_path + "max_bridge_length"); optgroup = page->new_optgroup(L("Organic supports")); const std::string path = "organic-supports_480131#organic-supports-settings";