diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index cc2b49be2fb..4f29a54fbb7 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -798,6 +798,12 @@ Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Pol { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset) +{ + Slic3r::ExPolygons clip_temp; + clip_temp.push_back(clip); + return diff_ex(subject, clip_temp, do_safety_offset); +} Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } @@ -817,6 +823,8 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset) + { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset);} // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type) { return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No, fill_type); } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 2b6fad550b4..5550f55c813 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -463,6 +463,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surf Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); @@ -498,6 +499,7 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 0998ece0693..c83d8ae8747 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -472,6 +472,7 @@ static std::vector s_Preset_print_options { "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode", "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "ensure_vertical_shell_thickness", "extra_perimeters", "extra_perimeters_on_overhangs", + "make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position", "staggered_inner_seams", "seam_gap_distance", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1ea05498a10..f142afb8a76 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -425,6 +425,7 @@ class PrintObject : public PrintObjectBaseWithState prepare_adaptive_infill_data( const std::vector>& surfaces_w_bottom_z) const; FillLightning::GeneratorPtr prepare_lightning_infill_data(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4f633db3ab3..ad27a4febaf 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -559,6 +559,35 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(100., true)); + def = this->add("make_overhang_printable", coBool); + def->label = L("Make overhangs printable"); + def->category = L("Quality"); + def->tooltip = L("Modify the geometry to print overhangs without support material."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("make_overhang_printable_angle", coFloat); + def->label = L("Make overhangs printable - Maximum angle"); + def->category = L("Quality"); + def->tooltip = L("Maximum angle of overhangs to allow after making more steep overhangs printable." + "90° will not change the model at all and allow any overhang, while 0 will " + "replace all overhangs with conical material."); + def->sidetext = u8"°"; // degrees, don't need translation + def->mode = comAdvanced; + def->min = 0.; + def->max = 90.; + def->set_default_value(new ConfigOptionFloat(55.)); + + def = this->add("make_overhang_printable_hole_size", coFloat); + def->label = L("Make overhangs printable - Hole area"); + def->category = L("Quality"); + def->tooltip = L("Maximum area of a hole in the base of the model before it's filled by conical material. " + "A value of 0 will fill all the holes in the model base."); + def->sidetext = u8"mm²"; // square millimeters, don't need translation + def->mode = comAdvanced; + def->min = 0.; + def->set_default_value(new ConfigOptionFloat(0.)); + // Maximum extruder temperature, bumped to 1500 to support printing of glass. const int max_temp = 1500; def = this->add("avoid_crossing_curled_overhangs", coBool); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 0b62925b907..c1eaffcae29 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -689,7 +689,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, thick_bridges)) ((ConfigOptionFloat, xy_size_compensation)) ((ConfigOptionBool, wipe_into_objects)) - + ((ConfigOptionFloat, make_overhang_printable_angle)) + ((ConfigOptionFloat, make_overhang_printable_hole_size)) ((ConfigOptionBool, interlocking_beam)) ((ConfigOptionFloat, interlocking_beam_width)) ((ConfigOptionFloat, interlocking_orientation)) @@ -763,6 +764,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, top_solid_min_thickness)) ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) ((ConfigOptionBool, wipe_into_infill)) + ((ConfigOptionBool, make_overhang_printable)) // Single perimeter. ((ConfigOptionEnum, top_one_perimeter_type)) ((ConfigOptionBool, only_one_perimeter_first_layer)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index dc0b6e454a0..818875dba34 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -761,6 +761,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "raft_contact_distance" || opt_key == "slice_closing_radius" || opt_key == "slicing_mode" + || opt_key == "make_overhang_printable" + || opt_key == "make_overhang_printable_angle" + || opt_key == "make_overhang_printable_hole_size" || opt_key == "interlocking_beam" || opt_key == "interlocking_orientation" || opt_key == "interlocking_beam_layer_count" diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 19fa3d96bd0..059ec58750e 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -892,6 +892,8 @@ void PrintObject::slice_volumes() m_layers.back()->upper_layer = nullptr; m_print->throw_if_canceled(); + this->apply_conical_overhang(); + // Is any ModelVolume multi-material painted? if (m_print->config().nozzle_diameter.size() > 1 && this->model_object()->is_mm_painted()) { // If XY Size compensation is also enabled, notify the user that XY Size compensation @@ -1011,6 +1013,124 @@ void PrintObject::slice_volumes() BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end"; } +void PrintObject::apply_conical_overhang() { + BOOST_LOG_TRIVIAL(info) << "Make overhang printable..."; + + if (m_layers.empty()) { + return; + } + + const double conical_overhang_angle = this->config().make_overhang_printable_angle; + if (conical_overhang_angle == 90.0) { + return; + } + const double angle_radians = conical_overhang_angle * M_PI / 180.; + const double max_hole_area = this->config().make_overhang_printable_hole_size; // in MM^2 + const double tan_angle = tan(angle_radians); // the XY-component of the angle + BOOST_LOG_TRIVIAL(info) << "angle " << angle_radians << " maxHoleArea " << max_hole_area << " tan_angle " + << tan_angle; + const coordf_t layer_thickness = m_config.layer_height.value; + const coordf_t max_dist_from_lower_layer = tan_angle * layer_thickness; // max dist which can be bridged, in MM + BOOST_LOG_TRIVIAL(info) << "layer_thickness " << layer_thickness << " max_dist_from_lower_layer " + << max_dist_from_lower_layer; + + // Pre-scale config + const coordf_t scaled_max_dist_from_lower_layer = -float(scale_(max_dist_from_lower_layer)); + const coordf_t scaled_max_hole_area = float(scale_(scale_(max_hole_area))); + + + for (auto i = m_layers.rbegin() + 1; i != m_layers.rend(); ++i) { + m_print->throw_if_canceled(); + Layer *layer = *i; + Layer *upper_layer = layer->upper_layer; + + if (upper_layer->empty()) { + continue; + } + + // Skip if entire layer has this disabled + if (std::all_of(layer->m_regions.begin(), layer->m_regions.end(), + [](const LayerRegion *r) { return r->slices().empty() || !r->region().config().make_overhang_printable; })) { + continue; + } + + //layer->export_region_slices_to_svg_debug("layer_before_conical_overhang"); + //upper_layer->export_region_slices_to_svg_debug("upper_layer_before_conical_overhang"); + + + // Merge the upper layer because we want to offset the entire layer uniformly, otherwise + // the model could break at the region boundary. + auto upper_poly = upper_layer->merged(float(SCALED_EPSILON)); + upper_poly = union_ex(upper_poly); + + // Merge layer for the same reason + auto current_poly = layer->merged(float(SCALED_EPSILON)); + current_poly = union_ex(current_poly); + + // Avoid closing up of recessed holes in the base of a model. + // Detects when a hole is completely covered by the layer above and removes the hole from the layer above before + // adding it in. + // This should have no effect any time a hole in a layer interacts with any polygon in the layer above + if (scaled_max_hole_area > 0.0) { + + // Now go through all the holes in the current layer and check if they intersect anything in the layer above + // If not, then they're the top of a hole and should be cut from the layer above before the union + for (auto layer_polygon : current_poly) { + for (auto hole : layer_polygon.holes) { + if (std::abs(hole.area()) < scaled_max_hole_area) { + ExPolygon hole_poly(hole); + auto hole_with_above = intersection_ex(upper_poly, hole_poly); + if (!hole_with_above.empty()) { + // The hole had some intersection with the above layer, check if it's a complete overlap + auto hole_difference = xor_ex(hole_with_above, hole_poly); + if (hole_difference.empty()) { + // The layer above completely cover it, remove it from the layer above + upper_poly = diff_ex(upper_poly, hole_poly); + } + } + } + } + } + } + + + // Now offset the upper layer to be added into current layer + upper_poly = offset_ex(upper_poly, scaled_max_dist_from_lower_layer); + + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { + // export_to_svg(debug_out_path("Surface-obj-%d-layer-%d-region-%d.svg", id().id, layer->id(), region_id).c_str(), + // layer->m_regions[region_id]->slices.surfaces); + + // Disable on given region + if (!upper_layer->m_regions[region_id]->region().config().make_overhang_printable) { + continue; + } + + // Calculate the scaled upper poly that belongs to current region + auto p = union_ex(intersection_ex(upper_layer->m_regions[region_id]->slices().surfaces, upper_poly)); + + // Remove all islands that have already been fully covered by current layer + p.erase(std::remove_if(p.begin(), p.end(), [¤t_poly](const ExPolygon& ex) { + return diff_ex(ex, current_poly).empty(); + }), p.end()); + + // And now union it with current region + ExPolygons layer_polygons = to_expolygons(layer->m_regions[region_id]->slices().surfaces); + layer->m_regions[region_id]->m_slices.set(union_ex(layer_polygons, p), stInternal); + + // Then remove it from all other regions, to avoid overlapping regions + for (size_t other_region = 0; other_region < this->num_printing_regions(); ++other_region) { + if (other_region == region_id) { + continue; + } + ExPolygons s = to_expolygons(layer->m_regions[other_region]->slices().surfaces); + layer->m_regions[other_region]->m_slices.set(diff_ex(s, p, ApplySafetyOffset::Yes), stInternal); + } + } + //layer->export_region_slices_to_svg_debug("layer_after_conical_overhang"); + } +} + std::vector PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const { auto it_volume = this->model_object()->volumes.begin(); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index b838a267d83..dfa14ad0929 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -411,6 +411,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("min_bead_width", have_arachne); toggle_field("thin_walls", !have_arachne); + bool have_make_overhang_printable = config->opt_bool("make_overhang_printable"); + toggle_field("make_overhang_printable_angle", have_make_overhang_printable); + toggle_field("make_overhang_printable_hole_size", have_make_overhang_printable); + toggle_field("scarf_seam_placement", !has_spiral_vase); const auto scarf_seam_placement{config->opt_enum("scarf_seam_placement")}; const bool uses_scarf_seam{!has_spiral_vase && scarf_seam_placement != ScarfSeamPlacement::nowhere}; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 176aa3cf862..7242e88f64b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1708,7 +1708,11 @@ void TabPrint::build() optgroup->append_single_option_line("gcode_resolution"); optgroup->append_single_option_line("arc_fitting"); optgroup->append_single_option_line("xy_size_compensation"); - optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487"); + + optgroup = page->new_optgroup(L("Overhangs")); + optgroup->append_single_option_line("make_overhang_printable"); + optgroup->append_single_option_line("make_overhang_printable_angle"); + optgroup->append_single_option_line("make_overhang_printable_hole_size"); optgroup = page->new_optgroup(L("Arachne perimeter generator")); optgroup->append_single_option_line("wall_transition_angle");