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:
parent
6f298ac6f1
commit
6c489808a7
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, ®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<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(®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<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[®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<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));
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue