ENH: speedup tree support generation

1. speedup detect_overhangs by skipping sharp tail and cantilever detection if there are too many overhangs.
   jira: STUDIO-3584, STUDIO-2592
2. drop_nodes with precalculation of avoidance and tbb parallel_for_each for all nodes in each layer.
   jira: STUDIO-1814, STUDIO-2381, STUDIO-2639, STUDIO-5020,
3. don't show too many progress messages

Change-Id: Ia4897089c69c235fb7cd8e5fdcf4690086048b31
(cherry picked from commit 9c08e28b5b5342dfdde2c939fc953f143a42a59b)
(cherry picked from commit 9de69035a029374be477b74e67c96dd8235daafa)
This commit is contained in:
Arthur 2024-03-15 11:53:06 +08:00 committed by Noisyfox
parent 19107b5869
commit 0b671e8852
2 changed files with 158 additions and 105 deletions

View file

@ -15,8 +15,10 @@
#include <boost/log/trivial.hpp>
#include <tbb/concurrent_vector.h>
#include <tbb/concurrent_unordered_set.h>
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
@ -649,7 +651,8 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
const bool bridge_no_support = max_bridge_length > 0;
const bool support_critical_regions_only = config.support_critical_regions_only.value;
const bool config_remove_small_overhangs = config.support_remove_small_overhang.value;
bool config_remove_small_overhangs = config.support_remove_small_overhang.value;
bool config_detect_sharp_tails = g_config_support_sharp_tails;
const int enforce_support_layers = config.enforce_support_layers.value;
const double area_thresh_well_supported = SQ(scale_(6));
const double length_thresh_well_supported = scale_(6);
@ -763,6 +766,10 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled));
}
});
typedef std::chrono::high_resolution_clock clock_;
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::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
[&](const tbb::blocked_range<size_t>& range) {
@ -795,10 +802,18 @@ 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);
ExPolygons overhang_areas = diff_ex(curr_polys, lower_layer_offseted);
ts_layer->overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted));
if (is_auto(stype) && g_config_support_sharp_tails)
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
if (duration > 30 || ts_layer->overhang_areas.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;
continue;
}
if (is_auto(stype) && config_detect_sharp_tails)
{
// BBS detect sharp tail
for (const ExPolygon& expoly : curr_polys) {
@ -813,7 +828,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
ExPolygons overhang = diff_ex({ expoly }, lower_polys);
layer->sharp_tails.push_back(expoly);
layer->sharp_tails_height.push_back(layer->height);
append(overhang_areas, overhang);
append(ts_layer->overhang_areas, overhang);
if (!overhang.empty()) {
has_sharp_tails = true;
@ -825,31 +840,28 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
}
}
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
for (ExPolygon& poly : overhang_areas) {
ts_layer->overhang_areas.emplace_back(poly);
// check cantilever
{
// lower_layer_offset may be very small, so we need to do max and then add 0.1
auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_polys, scale_(std::max(extrusion_width,lower_layer_offset)+0.1)));
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;
// 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) {
auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted);
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;
}
}
}
@ -861,7 +873,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
return;
// check if the sharp tails should be extended higher
if (is_auto(stype) && g_config_support_sharp_tails) {
if (is_auto(stype) && config_detect_sharp_tails) {
for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
if (m_object->print()->canceled())
break;
@ -945,21 +957,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
}
}
// 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;
}
}
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);
@ -967,6 +964,21 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
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
cluster.is_sharp_tail = false;
@ -1403,7 +1415,7 @@ void TreeSupport::generate_toolpaths()
if (m_object->print()->canceled())
break;
m_object->print()->set_status(70, (boost::format(_u8L("Support: generate toolpath at layer %d")) % layer_id).str());
//m_object->print()->set_status(70, (boost::format(_u8L("Support: generate toolpath at layer %d")) % layer_id).str());
SupportLayer* ts_layer = m_object->get_support_layer(layer_id);
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
@ -1583,7 +1595,7 @@ void TreeSupport::generate()
// Generate overhang areas
profiler.stage_start(STAGE_DETECT_OVERHANGS);
m_object->print()->set_status(55, _u8L("Support: detect overhangs"));
m_object->print()->set_status(55, _u8L("Generating supports"));
detect_overhangs();
profiler.stage_finish(STAGE_DETECT_OVERHANGS);
@ -1604,7 +1616,7 @@ void TreeSupport::generate()
//Drop nodes to lower layers.
profiler.stage_start(STAGE_DROP_DOWN_NODES);
m_object->print()->set_status(60, _u8L("Support: propagate branches"));
m_object->print()->set_status(60, _u8L("Generating supports"));
drop_nodes(contact_nodes);
profiler.stage_finish(STAGE_DROP_DOWN_NODES);
@ -1612,22 +1624,14 @@ void TreeSupport::generate()
//Generate support areas.
profiler.stage_start(STAGE_DRAW_CIRCLES);
m_object->print()->set_status(65, _u8L("Support: draw polygons"));
m_object->print()->set_status(65, _u8L("Generating supports"));
draw_circles(contact_nodes);
profiler.stage_finish(STAGE_DRAW_CIRCLES);
for (auto& layer : contact_nodes)
{
for (SupportNode* p_node : layer)
{
delete p_node;
}
layer.clear();
}
contact_nodes.clear();
profiler.stage_start(STAGE_GENERATE_TOOLPATHS);
m_object->print()->set_status(69, _u8L("Support: generate toolpath"));
m_object->print()->set_status(69, _u8L("Generating supports"));
generate_toolpaths();
profiler.stage_finish(STAGE_GENERATE_TOOLPATHS);
@ -1677,6 +1681,13 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to
return radius;
}
coordf_t TreeSupport::get_radius(const SupportNode* node, coordf_t base_radius)
{
if (node->radius == 0)
node->radius = calc_branch_radius(base_radius, node->dist_mm_to_top, node->diameter_angle_scale_factor);
return node->radius;
}
ExPolygons TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr)
{
#if USE_SUPPORT_3D
@ -2017,7 +2028,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
}
m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str());
//m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str());
// join roof segments
double contact_dist_scaled = scale_(0.5);// scale_(m_slicing_params.gap_support_object);
@ -2204,7 +2215,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
std::map<const Polygon*, std::tuple<int, Point, Point>> holePropagationInfos;
for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) {
if (print->canceled()) break;
m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str());
//m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str());
const std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
@ -2338,14 +2349,10 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
const auto nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(m_object->config().support_interface_filament-1);
const auto support_line_width = config.support_line_width.get_abs_value(nozzle_diameter);
auto get_branch_angle = [this,&config](coordf_t radius) {
if (config.tree_support_branch_angle.value < 30.0) return config.tree_support_branch_angle.value;
return (radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0;
};
auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width, support_line_width](const SupportNode *node, int power = 1) {
double move_dist = node->max_move_dist;
if (node->max_move_dist == 0) {
if (node->radius == 0) node->radius = calc_branch_radius(branch_radius, node->dist_mm_to_top, diameter_angle_scale_factor);
node->radius = get_radius(node, branch_radius);
double angle = config.tree_support_branch_angle.value;
if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS)
angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0;
@ -2362,7 +2369,47 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
std::vector<LayerHeightData> &layer_heights = m_ts_data->layer_heights;
if (layer_heights.empty()) return;
std::unordered_set<SupportNode*> to_free_node_set;
// precalculate avoidance of all possible radii.
// This will cause computing more (radius, layer_nr) pairs, but it's worth to do so since we are doning this in parallel.
if (1) {
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> t0{ clock_::now() };
// get all the possible radiis
std::vector<std::set<coordf_t> > all_layer_radius(contact_nodes.size());
std::vector<std::set<coordf_t>> all_layer_node_dist(contact_nodes.size());
for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) {
if (layer_heights[layer_nr].height < EPSILON) continue;
auto& layer_radius = all_layer_radius[layer_nr];
auto& layer_node_dist = all_layer_node_dist[layer_nr];
for (auto* p_node : contact_nodes[layer_nr]) {
layer_node_dist.emplace(p_node->dist_mm_to_top);
}
size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr;
if (layer_nr_next <= contact_nodes.size() - 1 && layer_nr_next > 0) {
for (auto node_dist : layer_node_dist)
all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height);
}
for (auto node_dist : layer_node_dist) {
layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor));
}
}
// parallel pre-compute avoidance
tbb::parallel_for(tbb::blocked_range<size_t>(0, contact_nodes.size() - 1), [&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
for (auto node_radius : all_layer_radius[layer_nr]) {
m_ts_data->get_avoidance(node_radius, layer_nr);
get_collision(m_ts_data->m_xy_distance, layer_nr);
}
}
});
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
BOOST_LOG_TRIVIAL(debug) << "finish pre calculate_avoidance. before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size()
<< ", takes " << duration << " secs.";
}
m_spanning_trees.resize(contact_nodes.size());
//m_mst_line_x_layer_contour_caches.resize(contact_nodes.size());
@ -2382,11 +2429,11 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
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, (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 supports"));// (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];
std::unordered_map<Line, bool, LineHash> mst_line_x_layer_contour_cache;
tbb::concurrent_unordered_map<Line, bool, LineHash> mst_line_x_layer_contour_cache;
auto is_line_cut_by_contour = [&mst_line_x_layer_contour_cache,&layer_contours](Point a, Point b)
{
auto iter = mst_line_x_layer_contour_cache.find({ a, b });
@ -2408,7 +2455,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
};
//Group together all nodes for each part.
const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr];// m_ts_data->get_avoidance(0, layer_nr);
const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr];
std::vector<std::unordered_map<Point, SupportNode*, PointHash>> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part.
for (SupportNode* p_node : layer_contact_nodes)
{
@ -2480,23 +2527,24 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
#endif
for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++)
{
auto& nodes_this_part = nodes_per_part[group_index];
const MinimumSpanningTree& mst = spanning_trees[group_index];
//In the first pass, merge all nodes that are close together.
std::unordered_set<SupportNode*> to_delete;
for (const std::pair<const Point, SupportNode*>& entry : nodes_per_part[group_index])
{
tbb::concurrent_unordered_set<SupportNode*> to_delete;
std::vector<std::pair<const Point, SupportNode*>> nodes_vec(nodes_this_part.begin(), nodes_this_part.end());
tbb::parallel_for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair<const Point, SupportNode*>& entry) {
SupportNode* p_node = entry.second;
SupportNode& node = *p_node;
if (to_delete.find(p_node) != to_delete.end())
{
continue; //Delete this node (don't create a new node for it on the next layer).
return; //Delete this node (don't create a new node for it on the next layer).
}
const std::vector<Point>& neighbours = mst.adjacent_nodes(node.position);
if (node.type == ePolygon) {
// Remove all circle neighbours that are completely inside the polygon and merge them into this node.
for (const Point &neighbour : neighbours) {
SupportNode * neighbour_node = nodes_per_part[group_index][neighbour];
if (neighbour_node->type == ePolygon) continue;
SupportNode * neighbour_node = nodes_this_part[neighbour];
if(neighbour_node->type==ePolygon) return;
coord_t neighbour_radius = scale_(neighbour_node->radius);
Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius),
pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0);
@ -2512,14 +2560,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
}
}
else if (neighbours.size() == 1 && vsize2_with_unscale(neighbours[0] - node.position) < max_move_distance2 && mst.adjacent_nodes(neighbours[0]).size() == 1 &&
nodes_per_part[group_index][neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon
nodes_this_part[neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon
{
//Insert a completely new node and let both original nodes fade.
Point next_position = (node.position + neighbours[0]) / 2; //Average position of the two nodes.
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor);
auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next);
coordf_t next_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top+height_next, diameter_angle_scale_factor);
auto avoid_layer = get_avoidance(next_radius, layer_nr_next);
if (group_index == 0)
{
//Avoid collisions.
@ -2527,23 +2573,23 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
move_out_expolys(avoid_layer, next_position, radius_sample_resolution + EPSILON, max_move_between_samples);
}
SupportNode* neighbour = nodes_per_part[group_index][neighbours[0]];
SupportNode* node_;
SupportNode* neighbour = nodes_this_part[neighbours[0]];
SupportNode* node_parent;
if (p_node->parent && neighbour->parent)
node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top) ? p_node : neighbour;
node_parent = (node.dist_mm_to_top >= neighbour->dist_mm_to_top) ? p_node : neighbour;
else
node_ = p_node->parent ? p_node : neighbour;
node_parent = p_node->parent ? p_node : neighbour;
// Make sure the next pass doesn't drop down either of these (since that already happened).
node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node);
node_parent->merged_neighbours.push_front(node_parent == p_node ? neighbour : p_node);
const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position);
SupportNode * next_node = m_ts_data->create_node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_,
print_z_next, height_next);
SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, layer_nr_next, node_parent->support_roof_layers_below - 1, to_buildplate, node_parent,
print_z_next, height_next);
get_max_move_dist(next_node);
m_ts_data->m_mutex.lock();
contact_nodes[layer_nr_next].push_back(next_node);
to_delete.insert(neighbour);
to_delete.insert(p_node);
m_ts_data->m_mutex.unlock();
}
else if (neighbours.size() > 1) //Don't merge leaf nodes because we would then incur movement greater than the maximum move distance.
{
@ -2552,7 +2598,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
{
if (vsize2_with_unscale(neighbour - node.position) < /*max_move_distance2*/get_max_move_dist(&node,2))
{
SupportNode* neighbour_node = nodes_per_part[group_index][neighbour];
SupportNode* neighbour_node = nodes_this_part[neighbour];
if (neighbour_node->type == ePolygon) continue;
// only allow bigger node to merge smaller nodes. See STUDIO-6326
if(node.dist_mm_to_top < neighbour_node->dist_mm_to_top) continue;
@ -2565,15 +2611,16 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
}
}
}
);
//In the second pass, move all middle nodes.
for (const std::pair<const Point, SupportNode*>& entry : nodes_per_part[group_index])
{
tbb::parallel_for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair<const Point, SupportNode*>& entry) {
SupportNode* p_node = entry.second;
const SupportNode& node = *p_node;
if (to_delete.find(p_node) != to_delete.end())
{
continue;
return;
}
if (node.type == ePolygon) {
// polygon node do not merge or move
@ -2595,15 +2642,19 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
p_node, print_z_next, height_next);
next_node->max_move_dist = 0;
next_node->overhang = std::move(overhangs_next[index_biggest]);
m_ts_data->m_mutex.lock();
contact_nodes[layer_nr_next].emplace_back(next_node);
m_ts_data->m_mutex.unlock();
}
continue;
return;
}
//If the branch falls completely inside a collision area (the entire branch would be removed by the X/Y offset), delete it.
if (group_index > 0 && is_inside_ex(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position))
{
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor);
std::scoped_lock lock(m_ts_data->m_mutex);
const coordf_t branch_radius_node = get_radius(p_node, branch_radius);
Point to_outside = projection_onto(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position);
double dist2_to_outside = vsize2_with_unscale(node.position - to_outside);
if (dist2_to_outside >= branch_radius_node * branch_radius_node) //Too far inside.
@ -2616,21 +2667,21 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
to_delete.insert(p_node);
p_node->valid = false;
}
continue;
return;
}
// if the link between parent and current is cut by contours, mark current as bottom contact node
if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false)
{
to_delete.insert(p_node);
p_node->valid = false;
continue;
return;
}
}
Point next_layer_vertex = node.position;
Point move_to_neighbor_center;
std::vector<Point> moves;
std::vector<float> weights;
const std::vector<Point> neighbours = mst.adjacent_nodes(node.position);
const std::vector<Point>& neighbours = mst.adjacent_nodes(node.position);
// 1. do not merge neighbors under 5mm
// 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height]
float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position);
@ -2641,7 +2692,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
Point sum_direction(0, 0);
for (const Point &neighbour : neighbours) {
// do not move to the neighbor to be deleted
SupportNode *neighbour_node = nodes_per_part[group_index][neighbour];
SupportNode *neighbour_node = nodes_this_part[neighbour];
if (to_delete.find(neighbour_node) != to_delete.end()) continue;
Point direction = neighbour - node.position;
@ -2672,14 +2723,14 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
}
}
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top/*+node.print_z*/, diameter_angle_scale_factor);
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
if (node.position(1) > max_y) {
max_y = node.position(1);
branch_radius_temp = branch_radius_node;
branch_radius_temp = get_radius(p_node, branch_radius);
}
#endif
auto avoidance_next = get_avoidance(branch_radius_node, layer_nr_next);
coordf_t next_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + height_next, diameter_angle_scale_factor);
auto avoidance_next = get_avoidance(next_radius, layer_nr_next);
Point to_outside = projection_onto(avoidance_next, node.position);
Point direction_to_outer = to_outside - node.position;
@ -2693,7 +2744,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
Point candidate_vertex = node.position;
const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors.
// use get_collision instead of get_avoidance here (See STUDIO-4252)
bool is_outside = move_out_expolys(get_collision(branch_radius_node,layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples);
bool is_outside = move_out_expolys(get_collision(next_radius,layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples);
if (is_outside) {
direction_to_outer = candidate_vertex - node.position;
dist2_to_outer = vsize2_with_unscale(direction_to_outer);
@ -2733,8 +2784,11 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node,
print_z_next, height_next);
get_max_move_dist(next_node);
m_ts_data->m_mutex.lock();
contact_nodes[layer_nr_next].push_back(next_node);
m_ts_data->m_mutex.unlock();
}
);
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
@ -2833,8 +2887,6 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_
const int iterations = 100;
for (size_t k = 0; k < iterations; k++) {
for (size_t i = 1; i < pts.size() - 1; i++) {
size_t i2 = i >= 2 ? i - 2 : 0;
size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1;
Point pt = ( pts[i - 1] + pts[i] + pts[i + 1] ) / 3;
pts1[i] = pt;
radii1[i] = (radii[i - 1] + radii[i] + radii[i + 1] ) / 3;
@ -3325,7 +3377,7 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke
{
const auto& radius = key.radius;
const auto& layer_nr = key.layer_nr;
BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance on radius=" << radius << ", layer=" << layer_nr<<", recursion="<<key.recursions;
BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance on (radius,layer)= (" << radius << "," << layer_nr<<"), recursion="<<key.recursions;
constexpr auto max_recursion_depth = 100;
if (key.recursions <= max_recursion_depth*2) {
if (layer_nr == 0) {