ENH: improve supporting sharp tails of tree support

1. sharp tails are supported by a sparse set of contact points which are
easier to remove than previously dense surrounding support.
   Organic tree support also has this feature, including all other smart
overhang detection techniques (small overhang and cantilever detection),
with the cost of slightly longer time to detect overhangs.
2. improve supporting overhang contours by adding contact points along
   contours.
  jira: STUDIO-3876
2. remove some redundant data structure.

Change-Id: If7f595348506a14aba2d0132d23f97d3539c1e1f
(cherry picked from commit e3cce09b9db12ced2841045ffd337b1f35494e6c)
(cherry picked from commit 507345deb193d895d0813fc913f00b0def7e62f9)
This commit is contained in:
Arthur 2024-03-26 11:39:30 +08:00 committed by Noisyfox
parent 0b671e8852
commit 2577b9b3a6
5 changed files with 159 additions and 149 deletions

View file

@ -11,6 +11,7 @@
#include "ShortestPath.hpp"
#include "I18N.hpp"
#include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/placers/nfpplacer.hpp>
#include "TreeSupport3D.hpp"
#include <boost/log/trivial.hpp>
@ -629,18 +630,15 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
{
// overhangs are already detected
if (m_object->support_layer_count() >= m_object->layer_count())
return;
bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value);
if (!tree_support_enable && !check_support_necessity) {
BOOST_LOG_TRIVIAL(info) << "Tree support is disabled.";
return;
}
// Clear and create Tree Support Layers
m_object->clear_support_layers();
m_object->clear_tree_support_preview_cache();
create_tree_support_layers();
if (!tree_support_enable && !check_support_necessity)
return;
const PrintObjectConfig& config = m_object->config();
SupportType stype = support_type;
@ -764,6 +762,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
Layer* layer = m_object->get_layer(layer_nr);
// Filter out areas whose diameter that is smaller than extrusion_width, but we don't want to lose any details.
layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled));
layer->loverhangs.clear();
}
});
@ -771,6 +770,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> t0{ clock_::now() };
// main part of overhang detection can be parallel
tbb::concurrent_vector<ExPolygons> overhangs_all_layers(m_object->layer_count());
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++) {
@ -802,12 +802,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
ExPolygons& lower_polys = lower_layer->lslices_extrudable;
// normal overhang
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
ts_layer->overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted));
overhangs_all_layers[layer_nr] = std::move(diff_ex(curr_polys, lower_layer_offseted));
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
if (duration > 30 || ts_layer->overhang_areas.size() > 100) {
if (duration > 30 || overhangs_all_layers[layer_nr].size() > 100) {
BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration;
config_detect_sharp_tails = false;
config_remove_small_overhangs = false;
@ -825,18 +824,14 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
}
if (is_sharp_tail) {
ExPolygons overhang = diff_ex({ expoly }, lower_polys);
layer->sharp_tails.push_back(expoly);
layer->sharp_tails_height.push_back(layer->height);
append(ts_layer->overhang_areas, overhang);
layer->sharp_tails_height.push_back(0);
if (!overhang.empty()) {
has_sharp_tails = true;
has_sharp_tails = true;
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly });
SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly });
#endif
}
}
}
}
}
@ -844,7 +839,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
// check cantilever
// lower_layer_offset may be very small, so we need to do max and then add 0.1
lower_layer_offseted = offset_ex(lower_layer_offseted, scale_(std::max(extrusion_width - lower_layer_offset, 0.) + 0.1));
for (ExPolygon& poly : ts_layer->overhang_areas) {
for (ExPolygon& poly : overhangs_all_layers[layer_nr]) {
auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted);
Polygons cluster_boundary = to_polygons(cluster_boundary_ex);
if (cluster_boundary.empty()) continue;
@ -879,7 +874,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
break;
Layer* layer = m_object->get_layer(layer_nr);
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
Layer* lower_layer = layer->lower_layer;
if (!lower_layer)
continue;
@ -941,43 +935,32 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
} while (0);
if (is_sharp_tail) {
ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices);
layer->sharp_tails.push_back(expoly);
layer->sharp_tails_height.push_back( accum_height);
append(ts_layer->overhang_areas, overhang);
if (!overhang.empty())
has_sharp_tails = true;
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), overhang);
#endif
}
}
}
}
// group overhang clusters
for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
if (m_object->print()->canceled())
break;
Layer* layer = m_object->get_layer(layer_nr);
for (auto& overhang : overhangs_all_layers[layer_nr]) {
OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled);
if (overlaps({ overhang }, layer->cantilevers))
cluster->is_cantilever = true;
}
}
auto enforcers = m_object->slice_support_enforcers();
auto blockers = m_object->slice_support_blockers();
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers);
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers);
if (is_auto(stype) && config_remove_small_overhangs) {
if (blockers.size() < m_object->layer_count())
blockers.resize(m_object->layer_count());
// group overhang clusters
for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
if (m_object->print()->canceled())
break;
if (layer_nr + m_raft_layers >= m_object->support_layer_count())
break;
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
Layer* layer = m_object->get_layer(layer_nr);
for (auto& overhang : ts_layer->overhang_areas) {
OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled);
if (overlaps({ overhang }, layer->cantilevers))
cluster->is_cantilever = true;
}
}
// remove small overhangs
for (auto& cluster : overhangClusters) {
// 3. check whether the small overhang is sharp tail
@ -1010,51 +993,66 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
{ cluster.merged_poly,{"overhang", "blue", 0.5} },
{ cluster.is_cantilever? layer1->cantilevers: offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled), {cluster.is_cantilever ? "cantilever":"erode1","green",0.5}} });
#endif
if (!cluster.is_small_overhang)
continue;
for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
int layer_nr = it->first;
auto p_overhang = it->second;
blockers[layer_nr].push_back(p_overhang->contour);
}
}
}
has_overhangs = false;
for (auto& cluster : overhangClusters) {
if (cluster.is_small_overhang) continue;
// collect overhangs that's not small overhangs
for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
int layer_nr = it->first;
auto p_overhang = it->second;
m_object->get_layer(layer_nr)->loverhangs.emplace_back(*p_overhang);
}
}
int layers_with_overhangs = 0;
for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
if (m_object->print()->canceled())
break;
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
auto layer = m_object->get_layer(layer_nr);
auto lower_layer = layer->lower_layer;
if (support_critical_regions_only && is_auto(stype)) {
ts_layer->overhang_areas.clear();
if (lower_layer == nullptr)
ts_layer->overhang_areas = layer->sharp_tails;
else
ts_layer->overhang_areas = diff_ex(layer->sharp_tails, lower_layer->lslices);
append(ts_layer->overhang_areas, layer->cantilevers);
// add support for every 1mm height for sharp tails
ExPolygons sharp_tail_overhangs;
if (lower_layer == nullptr)
sharp_tail_overhangs = layer->sharp_tails;
else {
ExPolygons lower_layer_expanded = offset_ex(lower_layer->lslices_extrudable, SCALED_RESOLUTION);
for (size_t i = 0; i < layer->sharp_tails_height.size();i++) {
ExPolygons areas = diff_clipped({ layer->sharp_tails[i]}, lower_layer_expanded);
float accum_height = layer->sharp_tails_height[i];
if (!areas.empty() && int(accum_height * 10) % 5 == 0) {
append(sharp_tail_overhangs, areas);
has_sharp_tails = true;
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), areas);
#endif
}
}
}
if (support_critical_regions_only && is_auto(stype)) {
layer->loverhangs.clear(); // remove oridinary overhangs, only keep cantilevers and sharp tails (added later)
append(layer->loverhangs, layer->cantilevers);
}
if (layer_nr < blockers.size()) {
Polygons& blocker = blockers[layer_nr];
// Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise,
// which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right.
ts_layer->overhang_areas = diff_ex(ts_layer->overhang_areas, offset_ex(union_(blocker), scale_(radius_sample_resolution)));
layer->loverhangs = diff_ex(layer->loverhangs, offset_ex(union_(blocker), scale_(radius_sample_resolution)));
}
if (max_bridge_length > 0 && ts_layer->overhang_areas.size() > 0 && lower_layer) {
if (max_bridge_length > 0 && layer->loverhangs.size() > 0 && lower_layer) {
// do not break bridge for normal part in TreeHybrid, nor Tree Strong
bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang)
bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(layer->loverhangs) > m_support_params.thresh_big_overhang)
&& !(m_support_params.support_style==smsTreeStrong);
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge);
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &layer->loverhangs, max_bridge_length, break_bridge);
}
int nDetected = ts_layer->overhang_areas.size();
int nDetected = layer->loverhangs.size();
// enforcers now follow same logic as normal support. See STUDIO-3692
if (layer_nr < enforcers.size() && lower_layer) {
float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX,
@ -1065,31 +1063,36 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
ExPolygons enforcer_polygons = diff_ex(intersection_ex(layer->lslices, enforcer),
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
append(ts_layer->overhang_areas, enforcer_polygons);
append(layer->loverhangs, enforcer_polygons);
}
}
int nEnforced = ts_layer->overhang_areas.size();
int nEnforced = layer->loverhangs.size();
// add sharp tail overhangs
append(layer->loverhangs, sharp_tail_overhangs);
// fill overhang_types
for (size_t i = 0; i < ts_layer->overhang_areas.size(); i++)
ts_layer->overhang_types.emplace(&ts_layer->overhang_areas[i], i < nDetected ? SupportLayer::Detected :
i < nEnforced ? SupportLayer::Enforced : SupportLayer::SharpTail);
for (size_t i = 0; i < layer->loverhangs.size(); i++)
overhang_types.emplace(&layer->loverhangs[i], i < nDetected ? OverhangType::Detected :
i < nEnforced ? OverhangType::Enforced : OverhangType::SharpTail);
if (!ts_layer->overhang_areas.empty()) {
has_overhangs = true;
if (!layer->loverhangs.empty()) {
layers_with_overhangs++;
m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr));
}
if (!layer->cantilevers.empty()) has_cantilever = true;
}
BOOST_LOG_TRIVIAL(info) << "Tree support overhang detection done. " << layers_with_overhangs << " layers with overhangs.";
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
for (const SupportLayer* layer : m_object->support_layers()) {
if (layer->overhang_areas.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty()))
for (const Layer* layer : m_object->layers()) {
if (layer->loverhangs.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty()))
continue;
SVG::export_expolygons(debug_out_path("overhang_areas_%.2f.svg", layer->print_z), {
SVG::export_expolygons(debug_out_path("overhang_areas_%d_%.2f.svg",layer->id(), layer->print_z), {
{ m_object->get_layer(layer->id())->lslices, {"lslices","yellow",0.5} },
{ layer->overhang_areas, {"overhang","red",0.5} }
{ layer->loverhangs, {"overhang","red",0.5} }
});
if (enforcers.size() > layer->id()) {
@ -1595,12 +1598,11 @@ void TreeSupport::generate()
// Generate overhang areas
profiler.stage_start(STAGE_DETECT_OVERHANGS);
m_object->print()->set_status(55, _u8L("Generating supports"));
m_object->print()->set_status(55, _u8L("Generating support"));
detect_overhangs();
profiler.stage_finish(STAGE_DETECT_OVERHANGS);
if (!has_overhangs) return;
create_tree_support_layers();
m_ts_data = m_object->alloc_tree_support_preview_cache();
m_ts_data->is_slim = is_slim;
@ -1610,13 +1612,13 @@ void TreeSupport::generate()
std::vector<TreeSupport3D::SupportElements> move_bounds(m_highest_overhang_layer + 1);
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
m_object->print()->set_status(56, _u8L("Support: generate contact points"));
generate_contact_points(contact_nodes, move_bounds);
generate_contact_points(contact_nodes);
profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES);
//Drop nodes to lower layers.
profiler.stage_start(STAGE_DROP_DOWN_NODES);
m_object->print()->set_status(60, _u8L("Generating supports"));
m_object->print()->set_status(60, _u8L("Generating support"));
drop_nodes(contact_nodes);
profiler.stage_finish(STAGE_DROP_DOWN_NODES);
@ -1624,14 +1626,14 @@ void TreeSupport::generate()
//Generate support areas.
profiler.stage_start(STAGE_DRAW_CIRCLES);
m_object->print()->set_status(65, _u8L("Generating supports"));
m_object->print()->set_status(65, _u8L("Generating support"));
draw_circles(contact_nodes);
profiler.stage_finish(STAGE_DRAW_CIRCLES);
profiler.stage_start(STAGE_GENERATE_TOOLPATHS);
m_object->print()->set_status(69, _u8L("Generating supports"));
m_object->print()->set_status(70, _u8L("Generating support"));
generate_toolpaths();
profiler.stage_finish(STAGE_GENERATE_TOOLPATHS);
@ -1922,7 +1924,6 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
coordf_t max_layers_above_roof = 0;
coordf_t max_layers_above_roof1 = 0;
int interface_id = 0;
bool has_polygon_node = false;
bool has_circle_node = false;
bool need_extra_wall = false;
ExPolygons collision_sharp_tails;
@ -1943,6 +1944,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size();
//Draw the support areas and add the roofs appropriately to the support roof instead of normal areas.
ts_layer->lslices.reserve(contact_nodes[layer_nr].size());
ExPolygons area_poly; // the polygon node area which will be printed as normal support
for (const SupportNode* p_node : contact_nodes[layer_nr])
{
if (print->canceled())
@ -1959,9 +1961,9 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
else {
area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance));
}
if (node.type == ePolygon)
has_polygon_node = true;
area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top <= 0));
if (node.type == ePolygon)
area_poly = area;
}
else {
Polygon circle(branch_circle);
@ -2084,8 +2086,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
auto &area_groups = ts_layer->area_groups;
for (auto& area : ts_layer->base_areas) {
area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base);
area_groups.back().need_infill = has_polygon_node;
// area_groups.back().need_extra_wall = need_extra_wall;
area_groups.back().need_infill = overlaps({ area }, area_poly);
area_groups.back().need_extra_wall = need_extra_wall;
}
for (auto& area : ts_layer->roof_areas) {
area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof);
@ -2423,13 +2425,14 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
continue;
int layer_nr_next = layer_heights[layer_nr].next_layer_nr;
coordf_t print_z = layer_heights[layer_nr].print_z;
coordf_t print_z_next = layer_heights[layer_nr_next].print_z;
coordf_t height_next = layer_heights[layer_nr_next].height;
std::deque<std::pair<size_t, SupportNode*>> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches.
const Layer* ts_layer = m_object->get_support_layer(layer_nr);
m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating supports"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str());
m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str());
Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr));
//std::unordered_map<Line, bool, LineHash>& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr];
@ -3034,7 +3037,7 @@ std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::ve
return layer_heights;
}
void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes, const std::vector<TreeSupport3D::SupportElements>& move_bounds)
void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes)
{
const PrintObjectConfig &config = m_object->config();
const coordf_t point_spread = scale_(config.tree_support_branch_distance.value);
@ -3092,8 +3095,8 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
// fix bug of generating support for very thin objects
if (m_object->layers().size() <= z_distance_top_layers + 1)
return;
if (m_object->support_layer_count() <= m_raft_layers)
return;
//if (m_object->support_layer_count() <= m_raft_layers)
// return;
int nonempty_layers = 0;
tbb::concurrent_vector<Slic3r::Vec3f> all_nodes;
@ -3101,10 +3104,10 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
if (m_object->print()->canceled())
break;
auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
Layer* layer = m_object->get_layer(layer_nr);
auto& curr_nodes = contact_nodes[layer_nr - 1];
if (ts_layer->overhang_areas.empty()) continue;
if (layer->loverhangs.empty()) continue;
std::unordered_set<Point, PointHash> already_inserted;
auto print_z = m_object->get_layer(layer_nr)->print_z;
auto bottom_z = m_object->get_layer(layer_nr)->bottom_z();
@ -3138,9 +3141,9 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
return contact_node;
};
for (const auto& area_type : ts_layer->overhang_types) {
const auto& overhang_part = *area_type.first;
is_sharp_tail = area_type.second == SupportLayer::SharpTail;
for (const auto& overhang_part : layer->loverhangs) {
const auto& overhang_type = this->overhang_types[&overhang_part];
is_sharp_tail = overhang_type == OverhangType::SharpTail;
BoundingBox overhang_bounds = get_extents(overhang_part);
if (m_support_params.support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) {
if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr - 1])) {
@ -3169,55 +3172,42 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
}
}
}
auto& move_bounds_layer = move_bounds[layer_nr];
if (move_bounds_layer.empty()) {
overhang_bounds.inflated(half_overhang_distance);
bool added = false; //Did we add a point this way?
// add supports along contours
libnest2d::placers::EdgeCache<ExPolygon> edge_cache(overhang_part);
for (size_t i = 0; i < edge_cache.holeCount() + 1; i++) {
double step = point_spread / (i == 0 ? edge_cache.circumference() : edge_cache.circumference(i - 1));
double distance = 0;
while (distance < 1) {
auto pt = i == 0 ? edge_cache.coords(distance) : edge_cache.coords(i - 1, distance);
SupportNode* contact_node = insert_point(pt, overhang_part);
distance += step;
}
}
// don't add inner supports for sharp tails
if (is_sharp_tail)
continue;
// add inner supports
overhang_bounds.inflated(-radius);
ExPolygons overhang_inner = offset_ex(overhang_part, -radius);
for (Point candidate : grid_points) {
if (overhang_bounds.contains(candidate)) {
// BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported!
bool is_inside = is_inside_ex(overhang_part, candidate);
if (!is_inside) {
constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that
// fall between overhang areas on constant surfaces.
is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance);
}
bool is_inside = is_inside_ex(overhang_inner, candidate);
if (is_inside) {
SupportNode* contact_node = insert_point(candidate, overhang_part);
if (contact_node)
added = true;
}
}
}
if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported.
{
auto bbox = overhang_part.contour.bounding_box();
Points candidates;
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected)
candidates = { bbox.min, bounding_box_middle(bbox), bbox.max };
else
candidates = { bounding_box_middle(bbox) };
for (Point candidate : candidates) {
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
insert_point(candidate, overhang_part);
}
}
}
else {
for (auto& elem : move_bounds_layer) {
SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -gap_layers, layer_nr-1, support_roof_layers + 1, elem.state.to_buildplate,
SupportNode::NO_PARENT, print_z, height, z_distance_top);
contact_node->overhang = ExPolygon(elem.influence_area.front());
curr_nodes.emplace_back(contact_node);
}
}
}
if (!curr_nodes.empty()) nonempty_layers++;
for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); }
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), ts_layer->overhang_areas, m_ts_data->m_layer_outlines_below[layer_nr], {},
contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","outlines","" });
draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), layer->loverhangs,layer->lslices, m_ts_data->m_layer_outlines_below[layer_nr],
contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","lslices","outlines_below"});
#endif
}}
); // end tbb::parallel_for