diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 995df84587..415db25a96 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -885,6 +885,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu { PROFILE_CLEAR(); + // BBS + m_curr_print = print; + CNumericLocalesSetter locales_setter; // Does the file exist? If so, we hope that it is still valid. @@ -3655,7 +3658,8 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string Polyline travel { this->last_pos(), point }; // check whether a straight travel move would need retraction - bool needs_retraction = this->needs_retraction(travel, role); + LiftType lift_type = LiftType::SpiralLift; + bool needs_retraction = this->needs_retraction(travel, role, lift_type); // check whether wipe could be disabled without causing visible stringing bool could_be_wipe_disabled = false; // Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to. @@ -3670,10 +3674,15 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string && m_writer.is_current_position_clear()) { travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); // check again whether the new travel path still needs a retraction - needs_retraction = this->needs_retraction(travel, role); + needs_retraction = this->needs_retraction(travel, role, lift_type); //if (needs_retraction && m_layer_index > 1) exit(0); } + if (lift_type == LiftType::LazyLift) + printf("lazy lift\n"); + else if (lift_type == LiftType::SpiralLift) + printf("spiral lift\n"); + // Re-allow reduce_crossing_wall for the next travel moves m_avoid_crossing_perimeters.reset_once_modifiers(); @@ -3684,7 +3693,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string m_wipe.reset_path(); Point last_post_before_retract = this->last_pos(); - gcode += this->retract(); + gcode += this->retract(false, false, lift_type); // When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters. // Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction() // FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations. @@ -3719,35 +3728,100 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string return gcode; } -bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) +bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftType& lift_type) { if (travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel))) { // skip retraction if the move is shorter than the configured threshold return false; } + auto is_through_overhang = [this](const Polyline& travel) { + const float protect_z_scaled = scale_(0.4); + std::pair z_range; + z_range.second = m_layer ? m_layer->print_z : 0.f; + z_range.first = std::max(0.f, z_range.second - protect_z_scaled); + for (auto object : m_curr_print->objects()) { + BoundingBox obj_bbox = object->bounding_box(); + BoundingBox travel_bbox = get_extents(travel); + obj_bbox.offset(scale_(EPSILON)); + if (!obj_bbox.overlap(travel_bbox)) + continue; + + for (auto layer : object->layers()) { + if (layer->print_z < z_range.first) + continue; + + if (layer->print_z > z_range.second + EPSILON) + break; + + for (ExPolygon& overhang : layer->loverhangs) { + if (overhang.contains(travel)) + return true; + } + } + } + + return false; + }; + + auto to_lift_type = [](ZHopType z_hop_type) { + if (z_hop_type == ZHopType::zhtNormal) + return LiftType::NormalLift; + + if (z_hop_type == ZHopType::zhtSlope) + return LiftType::LazyLift; + + if (z_hop_type == ZHopType::zhtSpiral) + return LiftType::SpiralLift; + + // if no corresponding lift type, use normal lift + return LiftType::NormalLift; + }; + + float max_z_hop = 0.f; + for (int i = 0; i < m_config.z_hop.size(); i++) + max_z_hop = std::max(max_z_hop, (float)m_config.z_hop.get_at(i)); + float travel_len_thresh = max_z_hop / tan(GCodeWriter::slope_threshold); + float accum_len = 0.f; + Polyline clipped_travel; + for (auto line : travel.lines()) { + if (accum_len + line.length() > travel_len_thresh + EPSILON) { + Point end_pnt = line.a + line.normal() * (travel_len_thresh - accum_len); + clipped_travel.append(Polyline(line.a, end_pnt)); + break; + } + else { + clipped_travel.append(Polyline(line.a, line.b)); + accum_len += line.length(); + } + } + //BBS: force to retract when leave from external perimeter for a long travel //Better way is judging whether the travel move direction is same with last extrusion move. - if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter) + if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter) { + if (m_config.z_hop_type == ZHopType::zhtAuto) { + lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::LazyLift; + } + else { + lift_type = to_lift_type(m_config.z_hop_type); + } return true; + } if (role == erSupportMaterial || role == erSupportTransition) { const SupportLayer* support_layer = dynamic_cast(m_layer); - //FIXME support_layer->support_islands.contains should use some search structure! if (support_layer != NULL && support_layer->support_islands.contains(travel)) // skip retraction if this is a travel move inside a support material island //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material // at the end of the extrusion path! return false; - //reduce the retractions in lightning infills for tree support if (support_layer != NULL && support_layer->support_type==stInnerTree) for (auto &area : support_layer->base_areas) if (area.contains(travel)) return false; } - //BBS: need retract when long moving to print perimeter to avoid dropping of material if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr && m_config.sparse_infill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel)) @@ -3757,10 +3831,16 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) return false; // retract if reduce_infill_retraction is disabled or doesn't apply when role is perimeter + if (m_config.z_hop_type == ZHopType::zhtAuto) { + lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::LazyLift; + } + else { + lift_type = to_lift_type(m_config.z_hop_type); + } return true; } -std::string GCode::retract(bool toolchange, bool is_last_retraction) +std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type) { std::string gcode; @@ -3784,7 +3864,7 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction) if (m_writer.extruder()->retraction_length() > 0) { // BBS: don't do lazy_lift when enable spiral vase size_t extruder_id = m_writer.extruder()->id(); - gcode += m_writer.lift(!m_spiral_vase ? LiftType::SpiralLift : LiftType::NormalLift); + gcode += m_writer.lift(!m_spiral_vase ? lift_type : LiftType::NormalLift); } return gcode; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 5289a6656c..0b898dd4bd 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -395,8 +395,9 @@ private: std::string extrude_support(const ExtrusionEntityCollection &support_fills); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); - bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); - std::string retract(bool toolchange = false, bool is_last_retraction = false); + // BBS: detect lift type in needs_retraction + bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type); + std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::SpiralLift); std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id, double print_z); std::set m_objsWithBrim; // indicates the objs with brim @@ -477,6 +478,7 @@ private: GCodeProcessor m_processor; // BBS + Print* m_curr_print = nullptr; unsigned int m_toolchange_count; coordf_t m_nominal_z; bool m_need_change_layer_lift_z = false; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index e752ee49ca..e0b0879e22 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -93,6 +93,8 @@ public: bool is_current_position_clear() const { return m_is_current_pos_clear; }; //BBS: static const bool full_gcode_comment; + //Radian threshold of slope for lazy lift and spiral lift; + static const double slope_threshold; private: // Extruders are sorted by their ID, so that binary search is possible. @@ -121,9 +123,6 @@ private: double m_x_offset{ 0 }; double m_y_offset{ 0 }; - //Radian threshold of slope for lazy lift and spiral lift; - static const double slope_threshold; - std::string _travel_to_z(double z, const std::string &comment); std::string _spiral_travel_to_z(double z, const Vec2d &ij_offset, const std::string &comment); std::string _retract(double length, double restart_extra, const std::string &comment); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 814c10ea92..753ffed60a 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -143,6 +143,9 @@ public: ExPolygons lslices; std::vector lslices_bboxes; + // BBS + ExPolygons loverhangs; + size_t region_count() const { return m_regions.size(); } const LayerRegion* get_region(int idx) const { return m_regions[idx]; } LayerRegion* get_region(int idx) { return m_regions[idx]; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 53a939fd34..b942af8847 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -789,7 +789,7 @@ static std::vector s_Preset_printer_options { "silent_mode", // BBS "scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode", - "nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", + "nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_type", //SoftFever "host_type", "print_host", "printhost_apikey", "printhost_cafile","printhost_port","printhost_authorization_type", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8a2b0ea6b5..aab2ee0fb7 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -224,6 +224,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n osteps.emplace_back(posSimplifyPath); osteps.emplace_back(posSimplifySupportPath); steps.emplace_back(psSkirtBrim); + } + else if (opt_key == "z_hop_type") { + osteps.emplace_back(posDetectOverhangsForLift); } else { // for legacy, if we can't handle this option let's invalidate all steps //FIXME invalidate all steps of all objects as well? @@ -1631,6 +1634,17 @@ void Print::process(bool use_cache) } } + // BBS + for (PrintObject* obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->detect_overhangs_for_lift(); + } + else { + if (obj->set_started(posDetectOverhangsForLift)) + obj->set_done(posDetectOverhangsForLift); + } + } + BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c5f9aa2801..13ee0c3e44 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -84,7 +84,10 @@ enum PrintStep { enum PrintObjectStep { posSlice, posPerimeters, posPrepareInfill, - posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, posCount, + posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, + // BBS + posDetectOverhangsForLift, + posCount, }; // A PrintRegion object represents a group of volumes to print @@ -445,6 +448,10 @@ private: void slice_volumes(); //BBS ExPolygons _shrink_contour_holes(double contour_delta, double hole_delta, const ExPolygons& polys) const; + // BBS + void detect_overhangs_for_lift(); + void clear_overhangs_for_lift(); + // Has any support (not counting the raft). void detect_surfaces_type(); void process_external_surfaces(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7443ccbea2..f6cae37d82 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -292,6 +292,14 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType{ }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType) +static const t_config_enum_values s_keys_map_ZHopType = { + { "Auto Lift", zhtAuto }, + { "Normal Lift", zhtNormal }, + { "Slope Lift", zhtSlope }, + { "Spiral Lift", zhtSpiral } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ZHopType) + static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology) { for (std::pair &kvp : options) @@ -2127,6 +2135,21 @@ void PrintConfigDef::init_fff_params() def->mode = comSimple; def->set_default_value(new ConfigOptionFloats { 0.4 }); + def = this->add("z_hop_type", coEnum); + def->label = L("Z Hop Type"); + def->tooltip = L(""); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("auto"); + def->enum_values.push_back("normal"); + def->enum_values.push_back("slope"); + def->enum_values.push_back("spiral"); + def->enum_labels.push_back(L("Auto")); + def->enum_labels.push_back(L("Normal")); + def->enum_labels.push_back(L("Slope")); + def->enum_labels.push_back(L("Spiral")); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum{ ZHopType::zhtSpiral }); + def = this->add("retract_restart_extra", coFloats); //def->label = L("Extra length on restart"); def->label = "Extra length on restart"; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 399deb617b..792eaf0b39 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -200,6 +200,15 @@ enum NozzleType { ntCount }; +// BBS +enum ZHopType { + zhtAuto = 0, + zhtNormal, + zhtSlope, + zhtSpiral, + zhtCount +}; + static std::string bed_type_to_gcode_string(const BedType type) { std::string type_str; @@ -812,6 +821,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, retraction_length)) ((ConfigOptionFloats, retract_length_toolchange)) ((ConfigOptionFloats, z_hop)) + // BBS + ((ConfigOptionEnum, z_hop_type)) ((ConfigOptionFloats, retract_restart_extra)) ((ConfigOptionFloats, retract_restart_extra_toolchange)) ((ConfigOptionFloats, retraction_speed)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ff76f68b1f..817e04c9c8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -409,6 +409,48 @@ void PrintObject::ironing() } } +// BBS +void PrintObject::clear_overhangs_for_lift() +{ + if (!m_shared_object) { + for (Layer* l : m_layers) + l->loverhangs.clear(); + } +} + +static const float g_min_overhang_percent_for_lift = 0.3f; + +void PrintObject::detect_overhangs_for_lift() +{ + if (this->set_started(posDetectOverhangsForLift)) { + const float min_overlap = m_config.line_width * g_min_overhang_percent_for_lift; + size_t num_layers = this->layer_count(); + size_t num_raft_layers = m_slicing_params.raft_layers(); + + m_print->set_status(78, L("Detect overhangs for auto-lift")); + + this->clear_overhangs_for_lift(); + + if (m_print->config().z_hop_type != ZHopType::zhtAuto) + return; + + tbb::spin_mutex layer_storage_mutex; + tbb::parallel_for(tbb::blocked_range(num_raft_layers + 1, num_layers), + [this, min_overlap](const tbb::blocked_range& range) + { + for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { + Layer& layer = *m_layers[layer_id]; + Layer& lower_layer = *layer.lower_layer; + + ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap))); + layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width))); + } + }); + + this->set_done(posDetectOverhangsForLift); + } +} + void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 16cb034e2e..7aa0e18c86 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3319,6 +3319,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/) optgroup = page->new_optgroup(L("Retraction"), L"param_retraction"); optgroup->append_single_option_line("retraction_length", "", extruder_idx); optgroup->append_single_option_line("z_hop", "", extruder_idx); + optgroup->append_single_option_line("z_hop_type", ""); optgroup->append_single_option_line("retraction_speed", "", extruder_idx); optgroup->append_single_option_line("deretraction_speed", "", extruder_idx); //optgroup->append_single_option_line("retract_restart_extra", "", extruder_idx);