ENH: boost is_support_necessary's performance

1. Parallelize the majority of overhang detection, leaving only a small
part of sharp tail detection as sequential. This strategy makes
is_support_necessary 10 times faster.
2. Use the overlaps function to detect overlapping, instead of using
intersection().empty()
3. Control the max recursion depth to prevent crashing due to too deep
recursion.

Jira: STUDIO-2445, STUDIO-2458
Change-Id: I35283da3e4a22d7afe251b804ce30b90a9d754df
(cherry picked from commit 1a6fedd1a0c82906f1807234ea1b816247ca6fd7)
This commit is contained in:
arthur.tang 2023-03-14 17:04:42 +08:00 committed by Lane.Wei
parent 6f298ac6f1
commit 6c489808a7
5 changed files with 280 additions and 325 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> t0{ clock_::now() };
SupportNecessaryType sntype = this->is_support_necessary();
double duration{ std::chrono::duration_cast<second_>(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;

View File

@ -12,8 +12,6 @@
#include "I18N.hpp"
#include <libnest2d/backends/libslic3r/geometries.hpp>
#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<int, const ExPolygon*> 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, &region);
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<OverhangCluster> overhangClusters;
std::map<const ExPolygon*, int> overhang2clusterInd;
// for sharp tail detection
struct RegionCluster {
std::map<int, const ExPolygon*> 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<RegionCluster> regionClusters;
std::map<const ExPolygon*, int> 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(&region, layer_nr);
region2clusterInd.emplace(&region, i);
if (cluster.push_back_if_intersects(region, layer_nr, offset)) {
found = true;
break;
}
}
if (!found) {
regionClusters.emplace_back(&region, layer_nr);
region2clusterInd.emplace(&region, 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<ExPolygon, int, ExPolygonComp> 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<size_t>(0, m_object->layer_count()),
[&](const tbb::blocked_range<size_t>& 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[&region]];
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<std::vector<Node*>>& 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<std::vector<Node*>>& 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<std::vector<Node *>> &contact_nodes)
@ -3303,31 +3237,23 @@ std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::ve
}
}
for (int i = layer_heights.size() - 1; i >= 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<tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash>::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));

View File

@ -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<coordf_t, size_t>;
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<coord_t>()(elem.first) ^ std::hash<coord_t>()(elem.second * 7919);
return std::hash<coord_t>()(elem.radius) ^ std::hash<coord_t>()(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<RadiusLayerPair, ExPolygons, RadiusLayerPairHash> m_collision_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash> m_avoidance_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_collision_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> 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,