diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 59a099651..c0763f00f 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -269,11 +269,10 @@ public: ExPolygons support_islands; // Extrusion paths for the support base and for the support interface and contacts. ExtrusionEntityCollection support_fills; - SupportInnerType support_type = stInnerNormal; + SupportInnerType support_type = stInnerNormal; // for tree supports ExPolygons base_areas; - ExPolygons overhang_areas; // Is there any valid extrusion assigned to this LayerRegion? @@ -312,9 +311,7 @@ protected: bool need_extra_wall = false; AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {} }; - enum OverhangType { Detected = 0, Enforced, SharpTail }; std::vector area_groups; - std::map overhang_types; }; template diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 11b9f870d..f9622a6ed 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -801,10 +801,11 @@ void PrintObject::slice() std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy); m_print->throw_if_canceled(); //BBS: send warning message to slicing callback - if (!warning.empty()) { - BOOST_LOG_TRIVIAL(info) << warning; - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers); - } + // This warning is inaccurate, because the empty layers may have been replaced, or the model has supports. + //if (!warning.empty()) { + // BOOST_LOG_TRIVIAL(info) << warning; + // this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers); + //} #endif // BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 3c40c6df3..d0aecc7f1 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -12,6 +12,7 @@ #include "ShortestPath.hpp" #include "I18N.hpp" #include +#include #include "TreeModelVolumes.hpp" #include "TreeSupport3D.hpp" #include "SupportMaterial.hpp" @@ -653,18 +654,15 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) { - // overhangs are already detected - if (m_object->support_layer_count() >= m_object->layer_count()) - return; - bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value); + if (!tree_support_enable && !check_support_necessity) { + BOOST_LOG_TRIVIAL(info) << "Tree support is disabled."; + return; + } // Clear and create Tree Support Layers m_object->clear_support_layers(); m_object->clear_tree_support_preview_cache(); - create_tree_support_layers(); - if (!tree_support_enable && !check_support_necessity) - return; const PrintObjectConfig& config = m_object->config(); SupportType stype = support_type; @@ -787,6 +785,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) Layer* layer = m_object->get_layer(layer_nr); // Filter out areas whose diameter that is smaller than extrusion_width, but we don't want to lose any details. layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled)); + layer->loverhangs.clear(); } }); @@ -794,6 +793,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) typedef std::chrono::duration > second_; std::chrono::time_point t0{ clock_::now() }; // main part of overhang detection can be parallel + tbb::concurrent_vector overhangs_all_layers(m_object->layer_count()); tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { @@ -825,12 +825,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons& lower_polys = lower_layer->lslices_extrudable; // normal overhang - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - ts_layer->overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); + overhangs_all_layers[layer_nr] = std::move(diff_ex(curr_polys, lower_layer_offseted)); double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; - if (duration > 30 || ts_layer->overhang_areas.size() > 100) { + if (duration > 30 || overhangs_all_layers[layer_nr].size() > 100) { BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration; config_detect_sharp_tails = false; config_remove_small_overhangs = false; @@ -848,18 +847,14 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } if (is_sharp_tail) { - ExPolygons overhang = diff_ex({ expoly }, lower_polys); layer->sharp_tails.push_back(expoly); - layer->sharp_tails_height.push_back(layer->height); - append(ts_layer->overhang_areas, overhang); + layer->sharp_tails_height.push_back(0); - if (!overhang.empty()) { - has_sharp_tails = true; + has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); + SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); #endif - } - } + } } } @@ -867,7 +862,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) // check cantilever // lower_layer_offset may be very small, so we need to do max and then add 0.1 lower_layer_offseted = offset_ex(lower_layer_offseted, scale_(std::max(extrusion_width - lower_layer_offset, 0.) + 0.1)); - for (ExPolygon& poly : ts_layer->overhang_areas) { + for (ExPolygon& poly : overhangs_all_layers[layer_nr]) { auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted); Polygons cluster_boundary = to_polygons(cluster_boundary_ex); if (cluster_boundary.empty()) continue; @@ -902,7 +897,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) break; Layer* layer = m_object->get_layer(layer_nr); - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); Layer* lower_layer = layer->lower_layer; if (!lower_layer) continue; @@ -964,43 +958,32 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } while (0); if (is_sharp_tail) { - ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); layer->sharp_tails.push_back(expoly); layer->sharp_tails_height.push_back( accum_height); - append(ts_layer->overhang_areas, overhang); - - if (!overhang.empty()) - has_sharp_tails = true; -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), overhang); -#endif } } } } - + + // group overhang clusters + for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { + if (m_object->print()->canceled()) + break; + Layer* layer = m_object->get_layer(layer_nr); + for (auto& overhang : overhangs_all_layers[layer_nr]) { + OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); + if (overlaps({ overhang }, layer->cantilevers)) + cluster->is_cantilever = true; + } + } + auto enforcers = m_object->slice_support_enforcers(); auto blockers = m_object->slice_support_blockers(); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers); + if (is_auto(stype) && config_remove_small_overhangs) { - if (blockers.size() < m_object->layer_count()) - blockers.resize(m_object->layer_count()); - // group overhang clusters - for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { - if (m_object->print()->canceled()) - break; - if (layer_nr + m_raft_layers >= m_object->support_layer_count()) - break; - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - Layer* layer = m_object->get_layer(layer_nr); - for (auto& overhang : ts_layer->overhang_areas) { - OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); - if (overlaps({ overhang }, layer->cantilevers)) - cluster->is_cantilever = true; - } - } // remove small overhangs for (auto& cluster : overhangClusters) { // 3. check whether the small overhang is sharp tail @@ -1033,51 +1016,66 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) { cluster.merged_poly,{"overhang", "blue", 0.5} }, { cluster.is_cantilever? layer1->cantilevers: offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled), {cluster.is_cantilever ? "cantilever":"erode1","green",0.5}} }); #endif - - if (!cluster.is_small_overhang) - continue; - - for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) { - int layer_nr = it->first; - auto p_overhang = it->second; - blockers[layer_nr].push_back(p_overhang->contour); - } } } - has_overhangs = false; + for (auto& cluster : overhangClusters) { + if (cluster.is_small_overhang) continue; + // collect overhangs that's not small overhangs + for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) { + int layer_nr = it->first; + auto p_overhang = it->second; + m_object->get_layer(layer_nr)->loverhangs.emplace_back(*p_overhang); + } + } + + int layers_with_overhangs = 0; for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { if (m_object->print()->canceled()) break; - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); auto layer = m_object->get_layer(layer_nr); auto lower_layer = layer->lower_layer; - if (support_critical_regions_only && is_auto(stype)) { - ts_layer->overhang_areas.clear(); - if (lower_layer == nullptr) - ts_layer->overhang_areas = layer->sharp_tails; - else - ts_layer->overhang_areas = diff_ex(layer->sharp_tails, lower_layer->lslices); - append(ts_layer->overhang_areas, layer->cantilevers); + // add support for every 1mm height for sharp tails + ExPolygons sharp_tail_overhangs; + if (lower_layer == nullptr) + sharp_tail_overhangs = layer->sharp_tails; + else { + ExPolygons lower_layer_expanded = offset_ex(lower_layer->lslices_extrudable, SCALED_RESOLUTION); + for (size_t i = 0; i < layer->sharp_tails_height.size();i++) { + ExPolygons areas = diff_clipped({ layer->sharp_tails[i]}, lower_layer_expanded); + float accum_height = layer->sharp_tails_height[i]; + if (!areas.empty() && int(accum_height * 10) % 5 == 0) { + append(sharp_tail_overhangs, areas); + has_sharp_tails = true; +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), areas); +#endif + } + } + } + + if (support_critical_regions_only && is_auto(stype)) { + layer->loverhangs.clear(); // remove oridinary overhangs, only keep cantilevers and sharp tails (added later) + append(layer->loverhangs, layer->cantilevers); } if (layer_nr < blockers.size()) { Polygons& blocker = blockers[layer_nr]; // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, // which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right. - ts_layer->overhang_areas = diff_ex(ts_layer->overhang_areas, offset_ex(union_(blocker), scale_(radius_sample_resolution))); + layer->loverhangs = diff_ex(layer->loverhangs, offset_ex(union_(blocker), scale_(radius_sample_resolution))); } - if (max_bridge_length > 0 && ts_layer->overhang_areas.size() > 0 && lower_layer) { + if (max_bridge_length > 0 && layer->loverhangs.size() > 0 && lower_layer) { // do not break bridge for normal part in TreeHybrid, nor Tree Strong - bool break_bridge = !(support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang) + bool break_bridge = !(support_style == smsTreeHybrid && area(layer->loverhangs) > m_support_params.thresh_big_overhang) && !(support_style==smsTreeStrong); - m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); + m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &layer->loverhangs, max_bridge_length, break_bridge); } - int nDetected = ts_layer->overhang_areas.size(); + int nDetected = layer->loverhangs.size(); // enforcers now follow same logic as normal support. See STUDIO-3692 if (layer_nr < enforcers.size() && lower_layer) { float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX, @@ -1088,31 +1086,36 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons enforcer_polygons = diff_ex(intersection_ex(layer->lslices, enforcer), // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - append(ts_layer->overhang_areas, enforcer_polygons); + append(layer->loverhangs, enforcer_polygons); } } - int nEnforced = ts_layer->overhang_areas.size(); + int nEnforced = layer->loverhangs.size(); + // add sharp tail overhangs + append(layer->loverhangs, sharp_tail_overhangs); + // fill overhang_types - for (size_t i = 0; i < ts_layer->overhang_areas.size(); i++) - ts_layer->overhang_types.emplace(&ts_layer->overhang_areas[i], i < nDetected ? SupportLayer::Detected : - i < nEnforced ? SupportLayer::Enforced : SupportLayer::SharpTail); + for (size_t i = 0; i < layer->loverhangs.size(); i++) + overhang_types.emplace(&layer->loverhangs[i], i < nDetected ? OverhangType::Detected : + i < nEnforced ? OverhangType::Enforced : OverhangType::SharpTail); - if (!ts_layer->overhang_areas.empty()) { - has_overhangs = true; + if (!layer->loverhangs.empty()) { + layers_with_overhangs++; m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr)); } if (!layer->cantilevers.empty()) has_cantilever = true; } + BOOST_LOG_TRIVIAL(info) << "Tree support overhang detection done. " << layers_with_overhangs << " layers with overhangs."; + #ifdef SUPPORT_TREE_DEBUG_TO_SVG - for (const SupportLayer* layer : m_object->support_layers()) { - if (layer->overhang_areas.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) + for (const Layer* layer : m_object->layers()) { + if (layer->loverhangs.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) continue; - SVG::export_expolygons(debug_out_path("overhang_areas_%.2f.svg", layer->print_z), { + SVG::export_expolygons(debug_out_path("overhang_areas_%d_%.2f.svg",layer->id(), layer->print_z), { { m_object->get_layer(layer->id())->lslices, {"lslices","yellow",0.5} }, - { layer->overhang_areas, {"overhang","red",0.5} } + { layer->loverhangs, {"overhang","red",0.5} } }); if (enforcers.size() > layer->id()) { @@ -1628,19 +1631,20 @@ void TreeSupport::generate() // Generate overhang areas profiler.stage_start(STAGE_DETECT_OVERHANGS); - m_object->print()->set_status(55, _u8L("Generating supports")); + m_object->print()->set_status(55, _u8L("Generating support")); detect_overhangs(); profiler.stage_finish(STAGE_DETECT_OVERHANGS); + create_tree_support_layers(); m_ts_data = m_object->alloc_tree_support_preview_cache(); m_ts_data->is_slim = is_slim; // Generate contact points of tree support std::vector> contact_nodes(m_object->layers().size()); +#if USE_SUPPORT_3D std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); -#if USE_SUPPORT_3D //m_object->print()->set_status(56, _u8L("Support: precalculate avoidance")); Points bedpts = m_machine_border.contour.points; @@ -1666,19 +1670,19 @@ void TreeSupport::generate() if (support_style != smsTreeHybrid) { overhangs.resize(m_object->support_layer_count()); for (size_t i = 0; i < overhangs.size(); i++) { - overhangs[i] = to_polygons(m_object->get_support_layer(i)->overhang_areas); + overhangs[i] = to_polygons(m_object->get_layer(i)->loverhangs); } //m_object->clear_support_layers(); TreeSupport3D::generate_initial_areas(*m_object, *m_model_volumes.get(), tree_support_3d_config, overhangs, move_bounds, top_contacts, layer_storage, throw_on_cancel); } #endif - generate_contact_points(contact_nodes, move_bounds); + generate_contact_points(contact_nodes); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); - m_object->print()->set_status(60, _u8L("Generating supports")); + m_object->print()->set_status(60, _u8L("Generating support")); drop_nodes(contact_nodes); profiler.stage_finish(STAGE_DROP_DOWN_NODES); @@ -1686,14 +1690,14 @@ void TreeSupport::generate() //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); - m_object->print()->set_status(65, _u8L("Generating supports")); + m_object->print()->set_status(65, _u8L("Generating support")); draw_circles(contact_nodes); profiler.stage_finish(STAGE_DRAW_CIRCLES); profiler.stage_start(STAGE_GENERATE_TOOLPATHS); - m_object->print()->set_status(69, _u8L("Generating supports")); + m_object->print()->set_status(70, _u8L("Generating support")); generate_toolpaths(); profiler.stage_finish(STAGE_GENERATE_TOOLPATHS); @@ -1988,7 +1992,6 @@ void TreeSupport::draw_circles(const std::vector>& con coordf_t max_layers_above_roof = 0; coordf_t max_layers_above_roof1 = 0; int interface_id = 0; - bool has_polygon_node = false; bool has_circle_node = false; bool need_extra_wall = false; ExPolygons collision_sharp_tails; @@ -2009,6 +2012,7 @@ void TreeSupport::draw_circles(const std::vector>& con BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); + ExPolygons area_poly; // the polygon node area which will be printed as normal support for (const SupportNode* p_node : contact_nodes[layer_nr]) { if (print->canceled()) @@ -2025,9 +2029,9 @@ void TreeSupport::draw_circles(const std::vector>& con else { area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance)); } - if (node.type == ePolygon) - has_polygon_node = true; area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); + if (node.type == ePolygon) + area_poly = area; } else { Polygon circle(branch_circle); @@ -2154,8 +2158,8 @@ void TreeSupport::draw_circles(const std::vector>& con auto &area_groups = ts_layer->area_groups; for (auto& area : ts_layer->base_areas) { area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); - area_groups.back().need_infill = has_polygon_node; - area_groups.back().need_extra_wall = need_extra_wall; + area_groups.back().need_infill = overlaps({ area }, area_poly); + area_groups.back().need_extra_wall = need_extra_wall && !area_groups.back().need_infill; } for (auto& area : ts_layer->roof_areas) { area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); @@ -2491,13 +2495,14 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod continue; int layer_nr_next = layer_heights[layer_nr].next_layer_nr; + coordf_t print_z = layer_heights[layer_nr].print_z; coordf_t print_z_next = layer_heights[layer_nr_next].print_z; coordf_t height_next = layer_heights[layer_nr_next].height; std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. const Layer* ts_layer = m_object->get_support_layer(layer_nr); - m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating supports"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); + m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr)); //std::unordered_map& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; @@ -3226,7 +3231,7 @@ std::vector TreeSupport::plan_layer_heights(std::vector>& contact_nodes, const std::vector& move_bounds) +void TreeSupport::generate_contact_points(std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); @@ -3284,8 +3289,8 @@ void TreeSupport::generate_contact_points(std::vector> // fix bug of generating support for very thin objects if (m_object->layers().size() <= z_distance_top_layers + 1) return; - if (m_object->support_layer_count() <= m_raft_layers) - return; + //if (m_object->support_layer_count() <= m_raft_layers) + // return; int nonempty_layers = 0; tbb::concurrent_vector all_nodes; @@ -3293,10 +3298,9 @@ void TreeSupport::generate_contact_points(std::vector> for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { if (m_object->print()->canceled()) break; - auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); Layer* layer = m_object->get_layer(layer_nr); auto& curr_nodes = contact_nodes[layer_nr - 1]; - if (ts_layer->overhang_areas.empty()) continue; + if (layer->loverhangs.empty()) continue; std::unordered_set already_inserted; auto print_z = m_object->get_layer(layer_nr)->print_z; @@ -3332,9 +3336,9 @@ void TreeSupport::generate_contact_points(std::vector> return contact_node; }; - for (const auto& area_type : ts_layer->overhang_types) { - const auto& overhang_part = *area_type.first; - is_sharp_tail = area_type.second == SupportLayer::SharpTail; + for (const auto& overhang_part : layer->loverhangs) { + const auto& overhang_type = this->overhang_types[&overhang_part]; + is_sharp_tail = overhang_type == OverhangType::SharpTail; BoundingBox overhang_bounds = get_extents(overhang_part); if (support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) { if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr - 1])) { @@ -3364,58 +3368,41 @@ void TreeSupport::generate_contact_points(std::vector> } } - auto& move_bounds_layer = move_bounds[layer_nr]; - if (move_bounds_layer.empty()) { - overhang_bounds.inflated(half_overhang_distance); - bool added = false; //Did we add a point this way? + // add supports along contours + libnest2d::placers::EdgeCache edge_cache(overhang_part); + for (size_t i = 0; i < edge_cache.holeCount() + 1; i++) { + double step = point_spread / (i == 0 ? edge_cache.circumference() : edge_cache.circumference(i - 1)); + double distance = 0; + while (distance < 1) { + auto pt = i == 0 ? edge_cache.coords(distance) : edge_cache.coords(i - 1, distance); + SupportNode* contact_node = insert_point(pt, overhang_part); + distance += step; + } + } + + // don't add inner supports for sharp tails + if (is_sharp_tail) + continue; + + // add inner supports + overhang_bounds.inflated(-radius); + ExPolygons overhang_inner = offset_ex(overhang_part, -radius); for (Point candidate : grid_points) { if (overhang_bounds.contains(candidate)) { // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! - bool is_inside = is_inside_ex(overhang_part, candidate); - if (!is_inside) { - constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that - // fall between overhang areas on constant surfaces. - is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); - } + bool is_inside = is_inside_ex(overhang_inner, candidate); if (is_inside) { SupportNode* contact_node = insert_point(candidate, overhang_part); - if (contact_node) - added = true; } } } - - if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. - { - auto bbox = overhang_part.contour.bounding_box(); - Points candidates; - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) - candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; - else - candidates = { bounding_box_middle(bbox) }; - - for (Point candidate : candidates) { - if (!overhang_part.contains(candidate)) - move_inside_expoly(overhang_part, candidate); - insert_point(candidate, overhang_part); - } - } - } - else { - for (auto& elem : move_bounds_layer) { - SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -gap_layers, layer_nr-1, support_roof_layers + 1, elem.state.to_buildplate, - SupportNode::NO_PARENT, print_z, height, z_distance_top); - contact_node->overhang = ExPolygon(elem.influence_area.front()); - curr_nodes.emplace_back(contact_node); - - } - } + } if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG - draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), ts_layer->overhang_areas, m_ts_data->m_layer_outlines_below[layer_nr], {}, - contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","outlines","" }); + draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), layer->loverhangs,layer->lslices, m_ts_data->m_layer_outlines_below[layer_nr], + contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","lslices","outlines_below"}); #endif }} ); // end tbb::parallel_for diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 3328efd0e..09ecd1337 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -379,7 +379,6 @@ public: int avg_node_per_layer = 0; float nodes_angle = 0; - bool has_overhangs = false; bool has_sharp_tails = false; bool has_cantilever = false; double max_cantilever_dist = 0; @@ -396,6 +395,9 @@ public: * machine */ ExPolygon m_machine_border; + + enum OverhangType { Detected = 0, Enforced, SharpTail }; + std::map overhang_types; private: /*! * \brief Generator for model collision, avoidance and internal guide volumes @@ -414,6 +416,7 @@ private: size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; + float DO_NOT_MOVER_UNDER_MM = 0.0; coordf_t MAX_BRANCH_RADIUS = 10.0; coordf_t MIN_BRANCH_RADIUS = 0.5; @@ -476,7 +479,7 @@ private: * \return For each layer, a list of points where the tree should connect * with the model. */ - void generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds); + void generate_contact_points(std::vector>& contact_nodes); /*! * \brief Add a node to the next layer. diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 6434e29dc..36983cff2 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -4180,18 +4180,22 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons //FIXME generating overhangs just for the first mesh of the group. assert(processing.second.size() == 1); - //print.set_status(55, _L("Support: detect overhangs")); -#if 0 +#if 1 + // use smart overhang detection std::vector overhangs; tree_support->detect_overhangs(); const int num_raft_layers = int(config.raft_layers.size()); const int num_layers = int(print_object.layer_count()) + num_raft_layers; overhangs.resize(num_layers); - for (size_t i = 0; i < print_object.layer_count(); i++) - { - overhangs[i + num_raft_layers] = to_polygons(print_object.get_support_layer(i)->overhang_areas); + for (size_t i = 0; i < print_object.layer_count(); i++) { + for (ExPolygon& expoly : print_object.get_layer(i)->loverhangs) { + Polygons polys = to_polygons(expoly); + if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) { + polys = offset(to_polygons(expoly), scale_(0.2)); + } + append(overhangs[i + num_raft_layers], polys); + } } - print_object.clear_support_layers(); #else std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); #endif @@ -4274,7 +4278,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons #endif // TREESUPPORT_DEBUG_SVG // ### Propagate the influence areas downwards. This is an inherently serial operation. - //print.set_status(60, _L("Support: propagate branches")); + print.set_status(60, _L("Generating support")); create_layer_pathing(volumes, config, move_bounds, throw_on_cancel); auto t_path = std::chrono::high_resolution_clock::now(); @@ -4331,7 +4335,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons SupportGeneratorLayersPtr layers_sorted = generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); // Don't fill in the tree supports, make them hollow with just a single sheath line. - //print.set_status(69, _L("Support: generate toolpath")); + print.set_status(69, _L("Generating support")); generate_support_toolpaths(print_object, print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);