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:
parent
92d125c41f
commit
507345deb1
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue