mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 07:27:41 -06:00
ENH: improve cantilever detection's speed and accuracy
Cantilever detection should be done layer by layer, instead of using merged_poly of clusters. Jira: STUDIO-2198 Change-Id: Ifea017fc4c13425a944ee80f7dedb603a8e14cc2 (cherry picked from commit 0803a439a458ffeb9c6d91e90344a5b78794a6bd)
This commit is contained in:
parent
74e1ba7963
commit
3d710609d9
4 changed files with 94 additions and 117 deletions
|
@ -2657,7 +2657,7 @@ SupportNecessaryType PrintObject::is_support_necessary()
|
||||||
this->clear_support_layers();
|
this->clear_support_layers();
|
||||||
if (tree_support.has_sharp_tails)
|
if (tree_support.has_sharp_tails)
|
||||||
return SharpTail;
|
return SharpTail;
|
||||||
else if (tree_support.has_cantilever && tree_support.max_cantilevel_dist > cantilevel_dist_thresh)
|
else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh)
|
||||||
return Cantilever;
|
return Cantilever;
|
||||||
#endif
|
#endif
|
||||||
return NoNeedSupp;
|
return NoNeedSupp;
|
||||||
|
|
|
@ -1680,7 +1680,30 @@ static inline ExPolygons detect_overhangs(
|
||||||
} // for each layer.region
|
} // for each layer.region
|
||||||
}
|
}
|
||||||
|
|
||||||
return union_ex(overhang_polygons);
|
ExPolygons overhang_areas = union_ex(overhang_polygons);
|
||||||
|
// check cantilever
|
||||||
|
if (layer.lower_layer) {
|
||||||
|
for (ExPolygon& poly : overhang_areas) {
|
||||||
|
float fw = float(layer.regions().front()->flow(frExternalPerimeter).scaled_width());
|
||||||
|
auto cluster_boundary_ex = intersection_ex(poly, offset_ex(layer.lower_layer->lslices, scale_(0.5)));
|
||||||
|
Polygons cluster_boundary = to_polygons(cluster_boundary_ex);
|
||||||
|
if (cluster_boundary.empty()) continue;
|
||||||
|
double dist_max = 0;
|
||||||
|
for (auto& pt : poly.contour.points) {
|
||||||
|
double dist_pt = std::numeric_limits<double>::max();
|
||||||
|
for (auto& ply : cluster_boundary) {
|
||||||
|
double d = ply.distance_to(pt);
|
||||||
|
dist_pt = std::min(dist_pt, d);
|
||||||
|
}
|
||||||
|
dist_max = std::max(dist_max, dist_pt);
|
||||||
|
}
|
||||||
|
if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base
|
||||||
|
layer.cantilevers.emplace_back(poly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overhang_areas;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset
|
// Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset
|
||||||
|
@ -2154,19 +2177,21 @@ struct OverhangCluster {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void add_overhang(std::vector<OverhangCluster>& clusters, ExPolygon* overhang, int layer_nr, coordf_t offset_scaled) {
|
static OverhangCluster* add_overhang(std::vector<OverhangCluster>& clusters, ExPolygon* overhang, int layer_nr, coordf_t offset_scaled) {
|
||||||
|
OverhangCluster* cluster = nullptr;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < clusters.size(); i++) {
|
for (int i = 0; i < clusters.size(); i++) {
|
||||||
auto& cluster = clusters[i];
|
auto cluster_i = &clusters[i];
|
||||||
if (cluster.intersects(*overhang, layer_nr)) {
|
if (cluster_i->intersects(*overhang, layer_nr)) {
|
||||||
cluster.insert(overhang, layer_nr);
|
cluster_i->insert(overhang, layer_nr);
|
||||||
found = true;
|
cluster = cluster_i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!cluster) {
|
||||||
clusters.emplace_back(overhang, layer_nr, offset_scaled);
|
cluster = &clusters.emplace_back(overhang, layer_nr, offset_scaled);
|
||||||
}
|
}
|
||||||
|
return cluster;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate top contact layers supporting overhangs.
|
// Generate top contact layers supporting overhangs.
|
||||||
|
@ -2328,13 +2353,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||||
std::set<ExPolygon*> removed_overhang;
|
std::set<ExPolygon*> removed_overhang;
|
||||||
|
|
||||||
for (size_t layer_id = layer_id_start; layer_id < num_layers; layer_id++) {
|
for (size_t layer_id = layer_id_start; layer_id < num_layers; layer_id++) {
|
||||||
|
const Layer* layer = object.get_layer(layer_id);
|
||||||
for (auto& overhang : overhangs_per_layers[layer_id]) {
|
for (auto& overhang : overhangs_per_layers[layer_id]) {
|
||||||
add_overhang(clusters, &overhang, layer_id, fw_scaled);
|
OverhangCluster* cluster = add_overhang(clusters, &overhang, layer_id, fw_scaled);
|
||||||
|
if (overlaps({ overhang }, layer->cantilevers))
|
||||||
|
cluster->is_cantilever = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (OverhangCluster& cluster : clusters) {
|
for (OverhangCluster& cluster : clusters) {
|
||||||
|
|
||||||
// 3. check whether the small overhang is sharp tail
|
// 3. check whether the small overhang is sharp tail
|
||||||
cluster.is_sharp_tail = false;
|
cluster.is_sharp_tail = false;
|
||||||
for (size_t layer_id = cluster.min_layer; layer_id <= cluster.max_layer; layer_id++) {
|
for (size_t layer_id = cluster.min_layer; layer_id <= cluster.max_layer; layer_id++) {
|
||||||
|
@ -2344,33 +2371,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cluster.is_sharp_tail)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!cluster.is_sharp_tail) {
|
|
||||||
// 4. check whether the overhang cluster is cantilever (far awary from main body)
|
|
||||||
const Layer* layer = object.get_layer(cluster.min_layer);
|
|
||||||
if (layer->lower_layer == NULL) continue;
|
|
||||||
Layer* lower_layer = layer->lower_layer;
|
|
||||||
auto cluster_boundary = intersection(cluster.merged_overhangs_dilated, offset(lower_layer->lslices, scale_(0.5)));
|
|
||||||
if (cluster_boundary.empty()) continue;
|
|
||||||
double dist_max = 0;
|
|
||||||
Points cluster_pts;
|
|
||||||
for (auto& poly : cluster.merged_overhangs_dilated)
|
|
||||||
append(cluster_pts, poly.contour.points);
|
|
||||||
for (auto& pt : cluster_pts) {
|
|
||||||
double dist_pt = std::numeric_limits<double>::max();
|
|
||||||
for (auto& poly : cluster_boundary) {
|
|
||||||
double d = poly.distance_to(pt);
|
|
||||||
dist_pt = std::min(dist_pt, d);
|
|
||||||
}
|
|
||||||
dist_max = std::max(dist_max, dist_pt);
|
|
||||||
}
|
|
||||||
if (dist_max > scale_(3)) {
|
|
||||||
cluster.is_cantilever = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cluster.is_sharp_tail && !cluster.is_cantilever) {
|
if (!cluster.is_sharp_tail && !cluster.is_cantilever) {
|
||||||
// 2. check overhang cluster size is small
|
// 2. check overhang cluster size is small
|
||||||
|
|
|
@ -825,21 +825,24 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
std::vector<OverhangCluster> overhangClusters;
|
std::vector<OverhangCluster> overhangClusters;
|
||||||
|
|
||||||
auto find_and_insert_cluster = [](auto ®ionClusters, const ExPolygon ®ion, int layer_nr, coordf_t offset) {
|
auto find_and_insert_cluster = [](auto ®ionClusters, const ExPolygon ®ion, int layer_nr, coordf_t offset) {
|
||||||
bool found = false;
|
OverhangCluster *cluster = nullptr;
|
||||||
for (int i = 0; i < regionClusters.size(); i++) {
|
for (int i = 0; i < regionClusters.size(); i++) {
|
||||||
auto& cluster = regionClusters[i];
|
auto cluster_i = ®ionClusters[i];
|
||||||
if (cluster.push_back_if_intersects(region, layer_nr, offset)) {
|
if (cluster_i->push_back_if_intersects(region, layer_nr, offset)) {
|
||||||
found = true;
|
cluster = cluster_i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!cluster) {
|
||||||
regionClusters.emplace_back(®ion, layer_nr);
|
cluster = ®ionClusters.emplace_back(®ion, layer_nr);
|
||||||
}
|
}
|
||||||
|
return cluster;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!is_tree(stype)) return;
|
if (!is_tree(stype)) return;
|
||||||
|
|
||||||
|
max_cantilever_dist = 0;
|
||||||
|
|
||||||
// main part of overhang detection can be parallel
|
// main part of overhang detection can be parallel
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
|
||||||
[&](const tbb::blocked_range<size_t>& range) {
|
[&](const tbb::blocked_range<size_t>& range) {
|
||||||
|
@ -912,26 +915,43 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
|
|
||||||
if (!overhang.empty())
|
if (!overhang.empty())
|
||||||
has_sharp_tails = true;
|
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.draw(lower_layer->lslices, "red");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||||
for (ExPolygon& poly : overhang_areas) {
|
for (ExPolygon& poly : overhang_areas) {
|
||||||
if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty())
|
if (offset_ex(poly, -0.1 * extrusion_width_scaled).empty()) continue;
|
||||||
ts_layer->overhang_areas.emplace_back(poly);
|
ts_layer->overhang_areas.emplace_back(poly);
|
||||||
|
|
||||||
|
// check cantilever
|
||||||
|
{
|
||||||
|
auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_layer->lslices, scale_(0.5)));
|
||||||
|
Polygons cluster_boundary = to_polygons(cluster_boundary_ex);
|
||||||
|
if (cluster_boundary.empty()) continue;
|
||||||
|
double dist_max = 0;
|
||||||
|
for (auto& pt : poly.contour.points) {
|
||||||
|
double dist_pt = std::numeric_limits<double>::max();
|
||||||
|
for (auto& ply : cluster_boundary) {
|
||||||
|
double d = ply.distance_to(pt);
|
||||||
|
dist_pt = std::min(dist_pt, d);
|
||||||
|
}
|
||||||
|
dist_max = std::max(dist_max, dist_pt);
|
||||||
|
}
|
||||||
|
if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base
|
||||||
|
max_cantilever_dist = std::max(max_cantilever_dist, dist_max);
|
||||||
|
layer->cantilevers.emplace_back(poly);
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max;
|
||||||
|
has_cantilever = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
); // end tbb::parallel_for
|
); // end tbb::parallel_for
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "max_cantilever_dist=" << max_cantilever_dist;
|
||||||
|
|
||||||
// check if the sharp tails should be extended higher
|
// check if the sharp tails should be extended higher
|
||||||
if (is_auto(stype) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) {
|
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++) {
|
for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
|
||||||
|
@ -1026,8 +1046,11 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
if (m_object->print()->canceled())
|
if (m_object->print()->canceled())
|
||||||
break;
|
break;
|
||||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||||
|
Layer* layer = m_object->get_layer(layer_nr);
|
||||||
for (auto& overhang : ts_layer->overhang_areas) {
|
for (auto& overhang : ts_layer->overhang_areas) {
|
||||||
find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled);
|
OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled);
|
||||||
|
if (overlaps({ overhang },layer->cantilevers))
|
||||||
|
cluster->is_cantilever = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,9 +1058,9 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
auto blockers = m_object->slice_support_blockers();
|
auto blockers = m_object->slice_support_blockers();
|
||||||
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers);
|
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers);
|
||||||
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers);
|
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers);
|
||||||
|
if (is_auto(stype) && g_config_remove_small_overhangs) {
|
||||||
// check whether the overhang cluster is sharp tail or cantilever
|
if (blockers.size() < m_object->layer_count())
|
||||||
max_cantilevel_dist = 0;
|
blockers.resize(m_object->layer_count());
|
||||||
for (auto& cluster : overhangClusters) {
|
for (auto& cluster : overhangClusters) {
|
||||||
// 3. check whether the small overhang is sharp tail
|
// 3. check whether the small overhang is sharp tail
|
||||||
cluster.is_sharp_tail = false;
|
cluster.is_sharp_tail = false;
|
||||||
|
@ -1048,44 +1071,6 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cluster.is_sharp_tail) continue;
|
|
||||||
|
|
||||||
// check whether the overhang cluster is cantilever (far awary from main body)
|
|
||||||
Layer* layer = m_object->get_layer(cluster.min_layer);
|
|
||||||
if (layer->lower_layer == NULL) continue;
|
|
||||||
Layer* lower_layer = layer->lower_layer;
|
|
||||||
auto cluster_boundary = intersection(cluster.merged_poly, offset(lower_layer->lslices, scale_(0.5)));
|
|
||||||
if (cluster_boundary.empty()) continue;
|
|
||||||
double dist_max = 0;
|
|
||||||
Points cluster_pts;
|
|
||||||
for (auto& poly : cluster.merged_poly)
|
|
||||||
append(cluster_pts, poly.contour.points);
|
|
||||||
for (auto& pt : cluster_pts) {
|
|
||||||
double dist_pt = std::numeric_limits<double>::max();
|
|
||||||
for (auto& poly : cluster_boundary) {
|
|
||||||
double d = poly.distance_to(pt);
|
|
||||||
dist_pt = std::min(dist_pt, d);
|
|
||||||
}
|
|
||||||
dist_max = std::max(dist_max, dist_pt);
|
|
||||||
}
|
|
||||||
if (dist_max > scale_(3)) { // this cluster is cantilever if the farmost point is larger than 3mm away from base
|
|
||||||
for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
|
|
||||||
int layer_nr = it->first;
|
|
||||||
auto p_overhang = it->second;
|
|
||||||
m_object->get_layer(layer_nr)->cantilevers.emplace_back(*p_overhang);
|
|
||||||
}
|
|
||||||
max_cantilevel_dist = std::max(max_cantilevel_dist, dist_max);
|
|
||||||
cluster.is_cantilever = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "max_cantilevel_dist=" << max_cantilevel_dist;
|
|
||||||
|
|
||||||
if (is_auto(stype) && g_config_remove_small_overhangs) {
|
|
||||||
if (blockers.size() < m_object->layer_count())
|
|
||||||
blockers.resize(m_object->layer_count());
|
|
||||||
for (auto& cluster : overhangClusters) {
|
|
||||||
// 4. check whether the overhang cluster is cantilever or sharp tail
|
|
||||||
if (cluster.is_cantilever || cluster.is_sharp_tail) continue;
|
|
||||||
|
|
||||||
if (!cluster.is_sharp_tail && !cluster.is_cantilever) {
|
if (!cluster.is_sharp_tail && !cluster.is_cantilever) {
|
||||||
// 2. check overhang cluster size is smaller than 3.0 * fw_scaled
|
// 2. check overhang cluster size is smaller than 3.0 * fw_scaled
|
||||||
|
@ -1111,15 +1096,6 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
int layer_nr = it->first;
|
int layer_nr = it->first;
|
||||||
auto p_overhang = it->second;
|
auto p_overhang = it->second;
|
||||||
blockers[layer_nr].push_back(p_overhang->contour);
|
blockers[layer_nr].push_back(p_overhang->contour);
|
||||||
// auto dilate1 = offset_ex(*p_overhang, extrusion_width_scaled);
|
|
||||||
// auto erode1 = offset_ex(*p_overhang, -extrusion_width_scaled);
|
|
||||||
// Layer* layer = m_object->get_layer(layer_nr);
|
|
||||||
// auto inter_with_others = intersection_ex(dilate1, diff_ex(layer->lslices, *p_overhang));
|
|
||||||
//// the following cases are small overhangs:
|
|
||||||
//// 1) overhang is single line (erode1.empty()==true)
|
|
||||||
//// 2) overhang is not island (intersects with others)
|
|
||||||
// if (erode1.empty() && !inter_with_others.empty())
|
|
||||||
// blockers[layer_nr].push_back(p_overhang->contour);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1133,6 +1109,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
auto layer = m_object->get_layer(layer_nr);
|
auto layer = m_object->get_layer(layer_nr);
|
||||||
auto lower_layer = layer->lower_layer;
|
auto lower_layer = layer->lower_layer;
|
||||||
if (support_critical_regions_only) {
|
if (support_critical_regions_only) {
|
||||||
|
ts_layer->overhang_areas.clear();
|
||||||
if (lower_layer == nullptr)
|
if (lower_layer == nullptr)
|
||||||
ts_layer->overhang_areas = layer->sharp_tails;
|
ts_layer->overhang_areas = layer->sharp_tails;
|
||||||
else
|
else
|
||||||
|
@ -1177,7 +1154,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
if (layer->overhang_areas.empty())
|
if (layer->overhang_areas.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SVG svg(get_svg_filename(std::to_string(layer->print_z), "overhang_areas"), m_object->bounding_box());
|
SVG svg(format("SVG/overhang_areas_%s.svg", layer->print_z), m_object->bounding_box());
|
||||||
if (svg.is_opened()) {
|
if (svg.is_opened()) {
|
||||||
svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow");
|
svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow");
|
||||||
svg.draw(layer->overhang_areas, "red");
|
svg.draw(layer->overhang_areas, "red");
|
||||||
|
|
|
@ -381,7 +381,7 @@ public:
|
||||||
bool has_overhangs = false;
|
bool has_overhangs = false;
|
||||||
bool has_sharp_tails = false;
|
bool has_sharp_tails = false;
|
||||||
bool has_cantilever = false;
|
bool has_cantilever = false;
|
||||||
double max_cantilevel_dist = 0;
|
double max_cantilever_dist = 0;
|
||||||
SupportType support_type;
|
SupportType support_type;
|
||||||
SupportMaterialStyle support_style;
|
SupportMaterialStyle support_style;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue