diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 3b7cc91e5..53b4414cf 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -174,6 +174,17 @@ bool ExPolygon::overlaps(const ExPolygon &other) const other.contains(this->contour.points.front()); } +bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2) +{ + for (const ExPolygon& expoly1 : expolys1) { + for (const ExPolygon& expoly2 : expolys2) { + if (expoly1.overlaps(expoly2)) + return true; + } + } + return false; +} + void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const { Polygons pp = this->simplify_p(tolerance); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 83b264803..72bfc7865 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -451,6 +451,8 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc // however their contours may be rotated. bool expolygons_match(const ExPolygon &l, const ExPolygon &r); +bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2); + BoundingBox get_extents(const ExPolygon &expolygon); BoundingBox get_extents(const ExPolygons &expolygons); BoundingBox get_extents_rotated(const ExPolygon &poly, double angle); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 41e56bdf9..0988676e4 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -460,9 +460,20 @@ void PrintObject::generate_support_material() m_print->throw_if_canceled(); } else { // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled + m_print->set_status(50, L("Checking support necessity")); + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration > second_; + std::chrono::time_point t0{ clock_::now() }; + SupportNecessaryType sntype = this->is_support_necessary(); + + double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) + << "is_support_necessary took " << (duration / 60) << " minutes and " + << std::setprecision(3) + << std::fmod(duration, 60.0) << " seconds." << std::endl; + if (sntype != NoNeedSupp) { - m_print->set_status(50, L("Checking support necessity")); if (sntype == SharpTail) { std::string warning_message = format(L("It seems object %s has completely floating regions. Please re-orient the object or enable support generation."), this->model_object()->name); @@ -2655,7 +2666,7 @@ SupportNecessaryType PrintObject::is_support_necessary() #else TreeSupport tree_support(*this, m_slicing_params); tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection - tree_support.detect_overhangs(); + tree_support.detect_overhangs(true); this->clear_support_layers(); if (tree_support.has_sharp_tails) return SharpTail; diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 5d5479b1a..a4bdf4207 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -12,8 +12,6 @@ #include "I18N.hpp" #include -#include "Fill/FillBase.hpp" - #define _L(s) Slic3r::I18N::translate(s) #define USE_PLAN_LAYER_HEIGHTS 1 @@ -722,7 +720,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -void TreeSupport::detect_overhangs() +void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) { // overhangs are already detected if (m_object->support_layer_count() >= m_object->layer_count()) @@ -731,10 +729,8 @@ void TreeSupport::detect_overhangs() // Clear and create Tree Support Layers m_object->clear_support_layers(); m_object->clear_tree_support_preview_cache(); - create_tree_support_layers(); - const PrintObjectConfig& config = m_object->config(); SupportType stype = support_type; const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution; @@ -750,16 +746,13 @@ void TreeSupport::detect_overhangs() // a region is considered well supported if the number of layers below it exceeds this threshold const int thresh_layers_below = 10 / config.layer_height; double obj_height = m_object->size().z(); + double threshold_rad = (config.support_threshold_angle.value < EPSILON ? 30 : config.support_threshold_angle.value + 1) * M_PI / 180.; - struct ExPolygonComp { - bool operator()(const ExPolygon& a, const ExPolygon& b) const { - return &a < &b; - } - }; // for small overhang removal struct OverhangCluster { std::map layer_overhangs; ExPolygons merged_poly; + BoundingBox merged_bbox; int min_layer = 1e7; int max_layer = 0; coordf_t offset = 0; @@ -774,6 +767,7 @@ void TreeSupport::detect_overhangs() merged_poly = union_ex(merged_poly, dilate1); min_layer = std::min(min_layer, layer_nr); max_layer = std::max(max_layer, layer_nr); + merged_bbox.merge(get_extents(dilate1)); } int height() { return max_layer - min_layer + 1; @@ -786,283 +780,259 @@ void TreeSupport::detect_overhangs() this->offset = offset; auto dilate1 = offset_ex(region, offset); - return !intersection_ex(*overhang, dilate1).empty(); + BoundingBox bbox = get_extents(dilate1); + if (!merged_bbox.overlap(bbox)) + return false; + return overlaps({ *overhang }, dilate1); + } + // it's basically the combination of push_back and intersects, but saves an offset_ex + bool push_back_if_intersects(const ExPolygon& region, int layer_nr, coordf_t offset) { + bool is_intersect = false; + ExPolygons dilate1; + BoundingBox bbox; + do { + if (layer_nr < 1) break; + auto it = layer_overhangs.find(layer_nr - 1); + if (it == layer_overhangs.end()) break; + const ExPolygon* overhang = it->second; + + this->offset = offset; + dilate1 = offset_ex(region, offset); + if (dilate1.empty()) break; + bbox = get_extents(dilate1); + if (!merged_bbox.overlap(bbox)) + break; + is_intersect = overlaps({ *overhang }, dilate1); + } while (0); + if (is_intersect) { + layer_overhangs.emplace(layer_nr, ®ion); + merged_poly = union_ex(merged_poly, dilate1); + min_layer = std::min(min_layer, layer_nr); + max_layer = std::max(max_layer, layer_nr); + merged_bbox.merge(bbox); + } + return is_intersect; } }; std::vector overhangClusters; - std::map overhang2clusterInd; - // for sharp tail detection - struct RegionCluster { - std::map layer_regions; - ExPolygon base; - BoundingBox bbox; - int min_layer = 1e7; - int max_layer = 0; - RegionCluster(const ExPolygon* expoly, int layer_nr) { - push_back(expoly, layer_nr); - } - void push_back(const ExPolygon* expoly, int layer_nr) { - if (layer_regions.empty()) { - base = *expoly; - bbox = get_extents(base); - } - layer_regions.emplace(layer_nr, expoly); - bbox.merge(get_extents(*expoly)); - min_layer = std::min(min_layer, layer_nr); - max_layer = std::max(max_layer, layer_nr); - } - int height() { - return max_layer - min_layer + 1; - } - bool intersects(const ExPolygon& region, int layer_nr, coordf_t offset) { - if (layer_nr < 1) return false; - auto it = layer_regions.find(layer_nr - 1); - if (it == layer_regions.end()) return false; - const ExPolygon* region_lower = it->second; - return !intersection_ex(*region_lower, region).empty(); - } - }; - std::vector regionClusters; - std::map region2clusterInd; - auto find_and_insert_cluster = [](auto& regionClusters, auto& region2clusterInd, const ExPolygon& region, int layer_nr, coordf_t offset) { + auto find_and_insert_cluster = [](auto& regionClusters, const ExPolygon& region, int layer_nr, coordf_t offset) { bool found = false; for (int i = 0; i < regionClusters.size();i++) { auto& cluster = regionClusters[i]; - if (cluster.intersects(region, layer_nr, offset)) { - cluster.push_back(®ion, layer_nr); - region2clusterInd.emplace(®ion, i); + if (cluster.push_back_if_intersects(region, layer_nr, offset)) { found = true; break; } } if (!found) { regionClusters.emplace_back(®ion, layer_nr); - region2clusterInd.emplace(®ion, regionClusters.size() - 1); } }; - // main part of sharptail detections - if (is_tree(stype)) - { - double threshold_rad = (config.support_threshold_angle.value < EPSILON ? 30 : config.support_threshold_angle.value+1) * M_PI / 180.; - ExPolygons regions_well_supported; - std::map region_layers_below; - ExPolygons lower_overhang_dilated; - - for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++){ - if (m_object->print()->canceled()) - break; - - if (!is_auto(stype) && layer_nr > enforce_support_layers) - continue; - Layer* layer = m_object->get_layer(layer_nr); - for (auto& slice : layer->lslices) - find_and_insert_cluster(regionClusters, region2clusterInd, slice, layer_nr, 0); + if (!is_tree(stype)) return; - if (layer->lower_layer == nullptr) { - for (auto& slice : layer->lslices) { - auto bbox_size = get_extents(slice).size(); - if (/*slice.area() > area_thresh_well_supported || */ - (bbox_size.x()>length_thresh_well_supported && bbox_size.y()>length_thresh_well_supported)) - regions_well_supported.emplace_back(slice); - else if(g_config_support_sharp_tails){ - layer->sharp_tails.push_back(slice); - layer->sharp_tails_height.insert({ &slice, layer->height }); + // main part of overhang detection can be parallel + 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++) { + if (m_object->print()->canceled()) + break; + + if (!is_auto(stype) && layer_nr > enforce_support_layers) + continue; + + Layer* layer = m_object->get_layer(layer_nr); + + if (layer->lower_layer == nullptr) { + for (auto& slice : layer->lslices) { + auto bbox_size = get_extents(slice).size(); + if (!((bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported)) + && g_config_support_sharp_tails) { + layer->sharp_tails.push_back(slice); + layer->sharp_tails_height.insert({ &slice, layer->height }); + } + } + continue; + } + + Layer* lower_layer = layer->lower_layer; + coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad); + coordf_t support_offset_scaled = scale_(lower_layer_offset); + // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! + ExPolygons lower_polys; + for (const ExPolygon& expoly : lower_layer->lslices) { + if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { + lower_polys.emplace_back(expoly); + } + } + ExPolygons curr_polys; + for (const ExPolygon& expoly : layer->lslices) { + if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { + curr_polys.emplace_back(expoly); } } - continue; - } + // normal overhang + ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); + ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); + + overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), + [extrusion_width_scaled](ExPolygon& area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), + overhang_areas.end()); + + + ExPolygons overhangs_sharp_tail; + if (is_auto(stype) && g_config_support_sharp_tails) + { + // BBS detect sharp tail + const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; + auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; + for (ExPolygon& expoly : layer->lslices) { + bool is_sharp_tail = false; + // 1. nothing below + // this is a sharp tail region if it's small but non-ignorable + if (!overlaps({ expoly }, lower_polys)) { + is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly, -0.5 * extrusion_width_scaled).empty(); + } + + if (is_sharp_tail) { + ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); + layer->sharp_tails.push_back(expoly); + layer->sharp_tails_height.insert({ &expoly, layer->height }); + append(overhang_areas, overhang); + + if (!overhang.empty()) + has_sharp_tails = true; +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box()); + if (svg.is_opened()) svg.draw(overhang, "yellow"); +#endif + } + } + } + + + if (max_bridge_length > 0 && overhang_areas.size() > 0) { + // do not break bridge for normal part in TreeHybrid + bool break_bridge = !(config.support_style == smsTreeHybrid && area(overhang_areas) > m_support_params.thresh_big_overhang); + m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, break_bridge); + } + + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + for (ExPolygon& poly : overhang_areas) { + if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty()) + ts_layer->overhang_areas.emplace_back(poly); + } + + + + } + } + ); // end tbb::parallel_for + + // check if the sharp tails should be extended higher + if (is_auto(stype) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) { + 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); + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); Layer* lower_layer = layer->lower_layer; - coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad); - coordf_t support_offset_scaled = scale_(lower_layer_offset); - // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! + // skip if: + // 1) if the current layer is already detected as sharp tails + // 2) lower layer has no sharp tails + if (!lower_layer || layer->sharp_tails.empty() == false || lower_layer->sharp_tails.empty() == true) + continue; ExPolygons lower_polys; for (const ExPolygon& expoly : lower_layer->lslices) { if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { lower_polys.emplace_back(expoly); } } - ExPolygons curr_polys; - for (const ExPolygon& expoly : layer->lslices) { - if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { - curr_polys.emplace_back(expoly); - } - } - // normal overhang - ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); + // BBS detect sharp tail + const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; + auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; + for (ExPolygon& expoly : layer->lslices) { + bool is_sharp_tail = false; + float accum_height = layer->height; + do { + // 2. something below + // check whether this is above a sharp tail region. - overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), - [extrusion_width_scaled](ExPolygon &area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), - overhang_areas.end()); - + // 2.1 If no sharp tail below, this is considered as common region. + ExPolygons supported_by_lower = intersection_ex({ expoly }, lower_layer_sharptails); + if (supported_by_lower.empty()) { + is_sharp_tail = false; + break; + } - ExPolygons overhangs_sharp_tail; - if (is_auto(stype) && g_config_support_sharp_tails) - { + // 2.2 If sharp tail below, check whether it support this region enough. #if 0 - // detect sharp tail and add more supports around - for (auto& lower_region : lower_layer_offseted) { - auto radius = get_extents(lower_region).radius(); - auto out_of_well_supported_region = offset_ex(diff_ex({ lower_region }, regions_well_supported), -extrusion_width_scaled); - auto bbox_size = get_extents(out_of_well_supported_region).size(); - double area_inter = area(intersection_ex({ lower_region }, regions_well_supported)); - if ((area_inter==0 || - ((bbox_size.x()> extrusion_width_scaled && bbox_size.y() > extrusion_width_scaled) && area_inter < area_thresh_well_supported ) ) - /*&& (obj_height - scale_(layer->slice_z)) > get_extents(lower_region).radius() * 5*/) { - auto lower_region_unoffseted = offset_ex(lower_region, -support_offset_scaled); - if (!lower_region_unoffseted.empty()) - lower_region = lower_region_unoffseted.front(); + // judge by area isn't reliable, failure cases include 45 degree rotated cube + float supported_area = area(supported_by_lower); + if (supported_area > area_thresh_well_supported) { + is_sharp_tail = false; + break; } - } - if (!lower_layer_offseted.empty()) { - overhangs_sharp_tail = std::move(diff_ex(curr_polys, lower_layer_offseted)); - //overhangs_sharp_tail = std::move(offset2_ex(overhangs_sharp_tail, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled)); - overhangs_sharp_tail = diff_ex(overhangs_sharp_tail, overhang_areas); - } - if (!overhangs_sharp_tail.empty()) { - append(layer->sharp_tails, overhangs_sharp_tail); - overhang_areas = union_ex(overhang_areas, overhangs_sharp_tail); - } -#else - // BBS detect sharp tail - const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; - auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; - for (ExPolygon& expoly : layer->lslices) { - bool is_sharp_tail = false; - float accum_height = layer->height; - do { - // 1. nothing below - // this is a sharp tail region if it's small but non-ignorable - if (intersection_ex({expoly}, lower_polys).empty()) { - is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*extrusion_width_scaled).empty(); - break; - } - - // 2. something below - // check whether this is above a sharp tail region. - - // 2.1 If no sharp tail below, this is considered as common region. - ExPolygons supported_by_lower = intersection_ex({expoly}, lower_layer_sharptails); - if (supported_by_lower.empty()) { - is_sharp_tail = false; - break; - } - - // 2.2 If sharp tail below, check whether it support this region enough. - float supported_area = area(supported_by_lower); - BoundingBox bbox = get_extents(supported_by_lower); -#if 0 - // judge by area isn't reliable, failure cases include 45 degree rotated cube - if (supported_area > area_thresh_well_supported) { - is_sharp_tail = false; - break; - } #endif - if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) { - is_sharp_tail = false; + BoundingBox bbox = get_extents(supported_by_lower); + if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) { + is_sharp_tail = false; + break; + } + + // 2.3 check whether sharp tail exceed the max height + for (auto& lower_sharp_tail_height : lower_layer_sharptails_height) { + if (lower_sharp_tail_height.first->overlaps(expoly)) { + accum_height += lower_sharp_tail_height.second; break; } + } + if (accum_height >= sharp_tail_max_support_height) { + is_sharp_tail = false; + break; + } - // 2.3 check whether sharp tail exceed the max height - for (auto &lower_sharp_tail_height : lower_layer_sharptails_height) { - if (!intersection_ex(*lower_sharp_tail_height.first, expoly).empty()) { - accum_height += lower_sharp_tail_height.second; - break; - } - } - if (accum_height >= sharp_tail_max_support_height) { - is_sharp_tail = false; - break; - } + // 2.4 if the area grows fast than threshold, it get connected to other part or + // it has a sharp slop and will be auto supported. + ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails); + if ((get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5), scale_(5)), ">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) { + is_sharp_tail = false; + break; + } - // 2.4 if the area grows fast than threshold, it get connected to other part or - // it has a sharp slop and will be auto supported. - ExPolygons new_overhang_expolys = diff_ex({expoly}, lower_layer_sharptails); - if ((get_extents(new_overhang_expolys).size()-get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) { - is_sharp_tail = false; - break; - } + // 2.5 mark the expoly as sharptail + is_sharp_tail = true; + } while (0); - // 2.5 mark the expoly as sharptail - is_sharp_tail = true; - } while (0); + if (is_sharp_tail) { + ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); + layer->sharp_tails.push_back(expoly); + layer->sharp_tails_height.insert({ &expoly, accum_height }); + append(ts_layer->overhang_areas, overhang); - if (is_sharp_tail) { - ExPolygons overhang = diff_ex({expoly}, lower_layer->lslices); - layer->sharp_tails.push_back(expoly); - layer->sharp_tails_height.insert({ &expoly, accum_height }); - append(overhang_areas, overhang); - - if (!overhang.empty()) - has_sharp_tails = true; + if (!overhang.empty()) + has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "yellow"); + SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box()); + if (svg.is_opened()) svg.draw(overhang, "yellow"); #endif - } - } -#endif - } - - - if (max_bridge_length > 0 && overhang_areas.size()>0) { - // do not break bridge for normal part in TreeHybrid - bool break_bridge = !(config.support_style == smsTreeHybrid && area(overhang_areas) > m_support_params.thresh_big_overhang); - m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, break_bridge); - } - - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - for (ExPolygon& poly : overhang_areas) { - if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty()) - ts_layer->overhang_areas.emplace_back(poly); - } - - if (is_auto(stype) && g_config_remove_small_overhangs) { - for (auto& overhang : ts_layer->overhang_areas) { - find_and_insert_cluster(overhangClusters, overhang2clusterInd, overhang, layer_nr, extrusion_width_scaled); - } - } - - if (is_auto(stype) && /*g_config_support_sharp_tails*/0) - { // update well supported regions - ExPolygons regions_well_supported2; - // regions intersects with lower regions_well_supported or large support are also well supported - auto inters = intersection_ex(layer->lslices, regions_well_supported); - auto inters2 = intersection_ex(layer->lslices, ts_layer->overhang_areas); - inters.insert(inters.end(), inters2.begin(), inters2.end()); - for (auto inter : inters) { - auto bbox_size = get_extents(inter).size(); - if (inter.area() >= area_thresh_well_supported - || (bbox_size.x()>length_thresh_well_supported && bbox_size.y()>length_thresh_well_supported) ) - { - auto tmp = offset_ex(inter, support_offset_scaled); - if (!tmp.empty()) { - // if inter is a single line with only 2 valid points, clipper will return empty - regions_well_supported2.emplace_back(std::move(tmp[0])); - } - } } - // experimental: regions high enough is also well supported - for (auto& region : layer->lslices) { - auto cluster = regionClusters[region2clusterInd[®ion]]; - if (layer_nr - cluster.min_layer > thresh_layers_below) - regions_well_supported2.push_back(region); - } - - regions_well_supported = union_ex(regions_well_supported2); -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - if (!regions_well_supported.empty()) { - SVG svg(get_svg_filename(std::to_string(layer->print_z), "regions_well_supported"), m_object->bounding_box()); - if (svg.is_opened()) - svg.draw(regions_well_supported, "yellow"); - } -#endif - } + } + } + } + + // group overhang clusters + for (size_t 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); + for (auto& overhang : ts_layer->overhang_areas) { + find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); } } @@ -1133,7 +1103,7 @@ void TreeSupport::detect_overhangs() bool is_sharp_tail = false; for (size_t layer_id = cluster.min_layer; layer_id <= cluster.max_layer; layer_id++) { Layer* layer = m_object->get_layer(layer_id); - if (!intersection_ex(layer->sharp_tails, cluster.merged_poly).empty()) { + if(overlaps(layer->sharp_tails, cluster.merged_poly)) { is_sharp_tail = true; break; } @@ -2488,21 +2458,6 @@ void TreeSupport::draw_circles(const std::vector>& contact_no char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); draw_contours_and_nodes_to_svg("", base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), {"base", "roof", "roof1st"}); } - - // export layer & print_z log - std::ofstream draw_circles_layer_out; - draw_circles_layer_out.open("./SVG/layer_heights_draw_circles.txt"); - if (draw_circles_layer_out.is_open()) { - for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - ExPolygons& base_areas = ts_layer->base_areas; - ExPolygons& roof_areas = ts_layer->roof_areas; - ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; - ExPolygons& floor_areas = ts_layer->floor_areas; - if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; - draw_circles_layer_out << layer_nr << " " << ts_layer->print_z << " " << ts_layer->height << std::endl; - } - } #endif // SUPPORT_TREE_DEBUG_TO_SVG SupportLayerPtrs& ts_layers = m_object->support_layers(); @@ -3046,27 +3001,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) delete node; } to_free_node_set.clear(); - - // Merge empty contact_nodes layers - - -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - // export all print_z and layer height into .txt - std::ofstream layer_heights_out; - layer_heights_out.open("./SVG/layer_heights_drop_nodes.txt"); - //layer_heights_out.open("layer_heights_out.txt"); - if (layer_heights_out.is_open()) { - for (int i = 0; i < layer_heights.size(); i++) { - if (contact_nodes[i].empty()) { - layer_heights_out << 0 << " " << 0 << std::endl; - } - else { - layer_heights_out << contact_nodes[i][0]->print_z << " " << contact_nodes[i][0]->height << std::endl; - } - } - layer_heights_out.close(); - } -#endif } void TreeSupport::smooth_nodes(std::vector> &contact_nodes) @@ -3303,31 +3237,23 @@ std::vector TreeSupport::plan_layer_heights(std::vector= 0; i--) { - if (layer_heights[i].height < EPSILON) continue; - for (int j = i - 1; j >= 0; j--) { + // fill in next_layer_nr + int i = layer_heights.size() - 1, j = i; + for (; j >= 0; i = j) { + if (layer_heights[i].height < EPSILON) { + j--; + continue; + } + for (j = i - 1; j >= 0; j--) { if (layer_heights[j].height > EPSILON) { layer_heights[i].next_layer_nr = j; break; } } + BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height, layer_nr->next_layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " + << i << "->" << layer_heights[i].next_layer_nr << std::endl; } -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - // check bounds - if (1) - { - std::ofstream bounds_out; - bounds_out.open("bounds.txt"); - if (bounds_out.is_open()) { - for (int i = 0; i < bounds.size(); i++) { - bounds_out << bounds[i] << std::endl; - } - } - } -#endif - for (int i = 0; i < layer_heights.size(); i++) { BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height: "<< layer_heights[i].print_z << " " << layer_heights[i].height << std::endl; } - return layer_heights; } @@ -3573,11 +3499,11 @@ const ExPolygons& TreeSupportData::get_collision(coordf_t radius, size_t layer_n return collision; } -const ExPolygons& TreeSupportData::get_avoidance(coordf_t radius, size_t layer_nr) const +const ExPolygons& TreeSupportData::get_avoidance(coordf_t radius, size_t layer_nr, int recursions) const { profiler.tic(); radius = ceil_radius(radius); - RadiusLayerPair key{radius, layer_nr}; + RadiusLayerPair key{radius, layer_nr, recursions }; const auto it = m_avoidance_cache.find(key); const ExPolygons& avoidance = it != m_avoidance_cache.end() ? it->second : calculate_avoidance(key); @@ -3624,22 +3550,20 @@ coordf_t TreeSupportData::ceil_radius(coordf_t radius) const const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& key) const { - const auto& radius = key.first; - const auto& layer_nr = key.second; + assert(key.layer_nr < m_layer_outlines.size()); - assert(layer_nr < m_layer_outlines.size()); - - ExPolygons collision_areas = std::move(offset_ex(m_layer_outlines[layer_nr], scale_(radius))); + ExPolygons collision_areas = std::move(offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius))); const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) }); return ret.first->second; } const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& key) const { - const auto& radius = key.first; - const auto& layer_nr = key.second; + const auto& radius = key.radius; + const auto& layer_nr = key.layer_nr; std::pair::iterator,bool> ret; - if (/*is_slim*/1) { + constexpr auto max_recursion_depth = 100; + if (key.recursions <= max_recursion_depth*2) { if (layer_nr == 0) { m_avoidance_cache[key] = get_collision(radius, 0); return m_avoidance_cache[key]; @@ -3650,19 +3574,17 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers // below our current one. - constexpr auto max_recursion_depth = 100; size_t layer_nr_next = layer_nr; - for (int i = 0; i < max_recursion_depth && layer_nr_next>0; i++) { - layer_nr_next = layer_heights[layer_nr_next].next_layer_nr; - } + int layers_below; + for (layers_below = 0; layers_below < max_recursion_depth && layer_nr_next > 0; layers_below++) { layer_nr_next = layer_heights[layer_nr_next].next_layer_nr; } // Check if we would exceed the recursion limit by trying to process this layer - if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) { + if (layers_below >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) { // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result. - get_avoidance(radius, layer_nr_next); + get_avoidance(radius, layer_nr_next, key.recursions + 1); } layer_nr_next = layer_heights[layer_nr].next_layer_nr; - ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr_next), scale_(-m_max_move))); + ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr_next, key.recursions+1), scale_(-m_max_move))); const ExPolygons &collision = get_collision(radius, layer_nr); avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end()); avoidance_areas = std::move(union_ex(avoidance_areas)); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index bb7d1ef47..f2498c8de 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -83,7 +83,7 @@ public: * \param layer The layer of interest * \return Polygons object */ - const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx) const; + const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const; Polygons get_contours(size_t layer_nr) const; Polygons get_contours_with_holes(size_t layer_nr) const; @@ -94,11 +94,20 @@ private: /*! * \brief Convenience typedef for the keys to the caches */ - using RadiusLayerPair = std::pair; - + struct RadiusLayerPair { + coordf_t radius; + size_t layer_nr; + int recursions; + + }; + struct RadiusLayerPairEquality { + constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const { + return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr; + } + }; struct RadiusLayerPairHash { size_t operator()(const RadiusLayerPair& elem) const { - return std::hash()(elem.first) ^ std::hash()(elem.second * 7919); + return std::hash()(elem.radius) ^ std::hash()(elem.layer_nr * 7919); } }; @@ -168,8 +177,8 @@ public: * coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for. * So we change to tbb::concurrent_unordered_map */ - mutable tbb::concurrent_unordered_map m_collision_cache; - mutable tbb::concurrent_unordered_map m_avoidance_cache; + mutable tbb::concurrent_unordered_map m_collision_cache; + mutable tbb::concurrent_unordered_map m_avoidance_cache; friend TreeSupport; }; @@ -203,7 +212,7 @@ public: */ void generate(); - void detect_overhangs(); + void detect_overhangs(bool detect_first_sharp_tail_only=false); enum NodeType { eCircle,