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)
This commit is contained in:
Arthur 2024-03-26 11:39:30 +08:00 committed by Lane.Wei
parent 92d125c41f
commit 507345deb1
5 changed files with 150 additions and 158 deletions

View File

@ -269,11 +269,10 @@ public:
ExPolygons support_islands;
// Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills;
SupportInnerType support_type = stInnerNormal;
SupportInnerType support_type = stInnerNormal;
// for tree supports
ExPolygons base_areas;
ExPolygons overhang_areas;
// Is there any valid extrusion assigned to this LayerRegion?
@ -312,9 +311,7 @@ protected:
bool need_extra_wall = false;
AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {}
};
enum OverhangType { Detected = 0, Enforced, SharpTail };
std::vector<AreaGroup> area_groups;
std::map<const ExPolygon *, OverhangType> overhang_types;
};
template<typename LayerContainer>

View File

@ -801,10 +801,11 @@ void PrintObject::slice()
std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy);
m_print->throw_if_canceled();
//BBS: send warning message to slicing callback
if (!warning.empty()) {
BOOST_LOG_TRIVIAL(info) << warning;
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers);
}
// This warning is inaccurate, because the empty layers may have been replaced, or the model has supports.
//if (!warning.empty()) {
// BOOST_LOG_TRIVIAL(info) << warning;
// this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers);
//}
#endif
// BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim

View File

@ -12,6 +12,7 @@
#include "ShortestPath.hpp"
#include "I18N.hpp"
#include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/placers/nfpplacer.hpp>
#include "TreeModelVolumes.hpp"
#include "TreeSupport3D.hpp"
#include "SupportMaterial.hpp"
@ -653,18 +654,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;
@ -787,6 +785,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();
}
});
@ -794,6 +793,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++) {
@ -825,12 +825,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;
@ -848,18 +847,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
}
}
}
}
}
@ -867,7 +862,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;
@ -902,7 +897,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;
@ -964,43 +958,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
@ -1033,51 +1016,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 = !(support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang)
bool break_bridge = !(support_style == smsTreeHybrid && area(layer->loverhangs) > m_support_params.thresh_big_overhang)
&& !(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,
@ -1088,31 +1086,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()) {
@ -1628,19 +1631,20 @@ 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);
create_tree_support_layers();
m_ts_data = m_object->alloc_tree_support_preview_cache();
m_ts_data->is_slim = is_slim;
// Generate contact points of tree support
std::vector<std::vector<SupportNode*>> contact_nodes(m_object->layers().size());
#if USE_SUPPORT_3D
std::vector<TreeSupport3D::SupportElements> move_bounds(m_highest_overhang_layer + 1);
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
#if USE_SUPPORT_3D
//m_object->print()->set_status(56, _u8L("Support: precalculate avoidance"));
Points bedpts = m_machine_border.contour.points;
@ -1666,19 +1670,19 @@ void TreeSupport::generate()
if (support_style != smsTreeHybrid) {
overhangs.resize(m_object->support_layer_count());
for (size_t i = 0; i < overhangs.size(); i++) {
overhangs[i] = to_polygons(m_object->get_support_layer(i)->overhang_areas);
overhangs[i] = to_polygons(m_object->get_layer(i)->loverhangs);
}
//m_object->clear_support_layers();
TreeSupport3D::generate_initial_areas(*m_object, *m_model_volumes.get(), tree_support_3d_config, overhangs, move_bounds, top_contacts, layer_storage, throw_on_cancel);
}
#endif
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);
@ -1686,14 +1690,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);
@ -1988,7 +1992,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;
@ -2009,6 +2012,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())
@ -2025,9 +2029,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);
@ -2154,8 +2158,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 && !area_groups.back().need_infill;
}
for (auto& area : ts_layer->roof_areas) {
area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof);
@ -2491,13 +2495,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];
@ -3226,7 +3231,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);
@ -3284,8 +3289,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;
@ -3293,10 +3298,9 @@ 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;
@ -3332,9 +3336,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 (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])) {
@ -3364,58 +3368,41 @@ 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

View File

@ -379,7 +379,6 @@ public:
int avg_node_per_layer = 0;
float nodes_angle = 0;
bool has_overhangs = false;
bool has_sharp_tails = false;
bool has_cantilever = false;
double max_cantilever_dist = 0;
@ -396,6 +395,9 @@ public:
* machine
*/
ExPolygon m_machine_border;
enum OverhangType { Detected = 0, Enforced, SharpTail };
std::map<const ExPolygon*, OverhangType> overhang_types;
private:
/*!
* \brief Generator for model collision, avoidance and internal guide volumes
@ -414,6 +416,7 @@ private:
size_t m_highest_overhang_layer = 0;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
float DO_NOT_MOVER_UNDER_MM = 0.0;
coordf_t MAX_BRANCH_RADIUS = 10.0;
coordf_t MIN_BRANCH_RADIUS = 0.5;
@ -476,7 +479,7 @@ private:
* \return For each layer, a list of points where the tree should connect
* with the model.
*/
void generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes, const std::vector<TreeSupport3D::SupportElements>& move_bounds);
void generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes);
/*!
* \brief Add a node to the next layer.

View File

@ -4180,18 +4180,22 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
//FIXME generating overhangs just for the first mesh of the group.
assert(processing.second.size() == 1);
//print.set_status(55, _L("Support: detect overhangs"));
#if 0
#if 1
// use smart overhang detection
std::vector<Polygons> overhangs;
tree_support->detect_overhangs();
const int num_raft_layers = int(config.raft_layers.size());
const int num_layers = int(print_object.layer_count()) + num_raft_layers;
overhangs.resize(num_layers);
for (size_t i = 0; i < print_object.layer_count(); i++)
{
overhangs[i + num_raft_layers] = to_polygons(print_object.get_support_layer(i)->overhang_areas);
for (size_t i = 0; i < print_object.layer_count(); i++) {
for (ExPolygon& expoly : print_object.get_layer(i)->loverhangs) {
Polygons polys = to_polygons(expoly);
if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) {
polys = offset(to_polygons(expoly), scale_(0.2));
}
append(overhangs[i + num_raft_layers], polys);
}
}
print_object.clear_support_layers();
#else
std::vector<Polygons> overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
#endif
@ -4274,7 +4278,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
#endif // TREESUPPORT_DEBUG_SVG
// ### Propagate the influence areas downwards. This is an inherently serial operation.
//print.set_status(60, _L("Support: propagate branches"));
print.set_status(60, _L("Generating support"));
create_layer_pathing(volumes, config, move_bounds, throw_on_cancel);
auto t_path = std::chrono::high_resolution_clock::now();
@ -4331,7 +4335,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
SupportGeneratorLayersPtr layers_sorted = generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
// Don't fill in the tree supports, make them hollow with just a single sheath line.
//print.set_status(69, _L("Support: generate toolpath"));
print.set_status(69, _L("Generating support"));
generate_support_toolpaths(print_object, print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);