NEW: add new tree support style "Tree Slim"

1. Add a new style "Tree Slim". If enabled, tree
support branches are merged more aggressively, and the support volume
is minimized.
2. Enable support style, add back Snug for normal support.
3. Remove hybrid(auto) type and use "Tree Hybrid" style to represent it.
4. Fix a bug in plan_layer_heights that may generate empty layers when
   layer height is set to 0.3mm.
5. Fix a bug where no raft is generated if there is no overhang.
6. Fix the bug where no bottom interface or bottom gap is generated when
   tree slim is selected.
7. Use physical distance to calc radius

Change-Id: Iacd57018ae5496cdc9acd28551c44d1c88c53fe0
This commit is contained in:
Arthur 2022-12-07 20:32:33 +08:00 committed by Lane.Wei
parent 51deb70f64
commit 29dbc77e91
16 changed files with 471 additions and 243 deletions

View File

@ -72,6 +72,7 @@
"enable_support": "0",
"resolution": "0.012",
"support_type": "normal(auto)",
"support_style": "default",
"support_on_build_plate_only": "0",
"support_top_z_distance": "0.2",
"support_bottom_z_distance": "0.2",

View File

@ -62,6 +62,7 @@
"enable_support": "0",
"resolution": "0.012",
"support_type": "normal(auto)",
"support_style": "default",
"support_on_build_plate_only": "0",
"support_top_z_distance": "0.2",
"support_filament": "0",

View File

@ -62,6 +62,7 @@
"enable_support": "0",
"resolution": "0.012",
"support_type": "normal(auto)",
"support_style": "default",
"support_on_build_plate_only": "0",
"support_top_z_distance": "0.2",
"support_filament": "0",

View File

@ -184,6 +184,12 @@ inline Point operator* (const Point& l, const double& r)
return { coord_t(l.x() * r), coord_t(l.y() * r) };
}
inline std::ostream &operator<<(std::ostream &os, const Point &pt)
{
os << unscale_(pt.x()) << "," << unscale_(pt.y());
return os;
}
inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON))
{
Point d = (p2 - p1).cwiseAbs();

View File

@ -352,6 +352,7 @@ public:
// Get a layer approximately at print_z.
const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon);
int get_layer_idx_get_printz(coordf_t print_z, coordf_t epsilon);
// BBS
const Layer* get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon) const;
Layer* get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon);

View File

@ -177,8 +177,12 @@ static t_config_enum_values s_keys_map_SupportMaterialPattern {
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern)
static t_config_enum_values s_keys_map_SupportMaterialStyle {
{ "default", smsDefault },
{ "grid", smsGrid },
{ "snug", smsSnug }
{ "snug", smsSnug },
{ "tree_slim", smsTreeSlim },
{ "tree_strong", smsTreeStrong },
{ "tree_hybrid", smsTreeHybrid }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
@ -192,7 +196,6 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialInterfacePattern)
static t_config_enum_values s_keys_map_SupportType{
{ "normal(auto)", stNormalAuto },
{ "tree(auto)", stTreeAuto },
{ "hybrid(auto)", stHybridAuto },
{ "normal(manual)", stNormal },
{ "tree(manual)", stTree }
};
@ -2369,16 +2372,14 @@ void PrintConfigDef::init_fff_params()
def->label = L("Type");
def->category = L("Support");
def->tooltip = L("normal(auto) and tree(auto) is used to generate support automatically. "
"If normal or tree is selected, only support enforcers are generated");
"If normal(manual) or tree(manual) is selected, only support enforcers are generated");
def->enum_keys_map = &ConfigOptionEnum<SupportType>::get_enum_values();
def->enum_values.push_back("normal(auto)");
def->enum_values.push_back("tree(auto)");
def->enum_values.push_back("hybrid(auto)");
def->enum_values.push_back("normal(manual)");
def->enum_values.push_back("tree(manual)");
def->enum_labels.push_back(L("normal(auto)"));
def->enum_labels.push_back(L("tree(auto)"));
def->enum_labels.push_back(L("hybrid(auto)"));
def->enum_labels.push_back(L("normal(manual)"));
def->enum_labels.push_back(L("tree(manual)"));
def->mode = comSimple;
@ -2417,7 +2418,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Support critical regions only");
def->category = L("Support");
def->tooltip = L("Only create support for critical regions including sharp tail, cantilever, etc.");
def->mode = comSimple;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
// BBS: change type to common float.
@ -2465,9 +2466,9 @@ void PrintConfigDef::init_fff_params()
def = this->add("support_filament", coInt);
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Support");
def->label = L("Support base");
def->category = L("Support");
def->tooltip = L("Filament to print support and raft. \"Default\" means no specific filament for support and current filament is used");
def->tooltip = L("Filament to print support base and raft. \"Default\" means no specific filament for support and current filament is used");
def->min = 0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionInt(1));
@ -2625,16 +2626,27 @@ void PrintConfigDef::init_fff_params()
def = this->add("support_style", coEnum);
def->label = L("Style");
def->category = L("Support");
//def->tooltip = L("Style and shape of the support towers. Projecting the supports into a regular grid "
// "will create more stable supports, while snug support towers will save material and reduce "
// "object scarring");
def->tooltip = L("Style and shape of the support. For normal support, projecting the supports into a regular grid "
"will create more stable supports (default), while snug support towers will save material and reduce "
"object scarring.\n"
"For tree support, tight style will merge branches more aggressively and save "
"a lot of material (default), while hybrid style will create similar structure to normal support "
"under large flat overhangs.");
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values();
def->enum_values.push_back("default");
def->enum_values.push_back("grid");
def->enum_values.push_back("snug");
def->enum_values.push_back("tree_slim");
def->enum_values.push_back("tree_strong");
def->enum_values.push_back("tree_hybrid");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("Grid"));
def->enum_labels.push_back(L("Snug"));
def->mode = comDevelop;
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
def->enum_labels.push_back(L("Tree Slim"));
def->enum_labels.push_back(L("Tree Strong"));
def->enum_labels.push_back(L("Tree Hybrid"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsDefault));
def = this->add("independent_support_layer_height", coBool);
def->label = L("Independent support layer height");
@ -3816,6 +3828,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
value = "normal(manual)";
} else if (opt_key == "support_type" && value == "tree") {
value = "tree(manual)";
} else if (opt_key == "support_type" && value == "hybrid(auto)") {
value = "tree(auto)";
} else if (opt_key == "different_settings_to_system") {
std::string copy_value = value;
copy_value.erase(std::remove(copy_value.begin(), copy_value.end(), '\"'), copy_value.end()); // remove '"' in string

View File

@ -98,7 +98,7 @@ enum SupportMaterialPattern {
};
enum SupportMaterialStyle {
smsGrid, smsSnug,
smsDefault, smsGrid, smsSnug, smsTreeSlim, smsTreeStrong, smsTreeHybrid
};
enum SupportMaterialInterfacePattern {
@ -107,7 +107,19 @@ enum SupportMaterialInterfacePattern {
// BBS
enum SupportType {
stNormalAuto, stTreeAuto, stHybridAuto, stNormal, stTree
stNormalAuto, stTreeAuto, stNormal, stTree
};
inline bool is_tree(SupportType stype)
{
return std::set<SupportType>{stTreeAuto, stTree}.count(stype) != 0;
};
inline bool is_tree_slim(SupportType type, SupportMaterialStyle style)
{
return is_tree(type) && (style==smsDefault || style==smsTreeSlim);
};
inline bool is_auto(SupportType stype)
{
return std::set<SupportType>{stNormalAuto, stTreeAuto}.count(stype) != 0;
};
enum SeamPosition {

View File

@ -2864,7 +2864,11 @@ const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
return (it == m_layers.begin()) ? nullptr : *(--it);
}
int PrintObject::get_layer_idx_get_printz(coordf_t print_z, coordf_t epsilon) {
coordf_t limit = print_z + epsilon;
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
return (it == m_layers.begin()) ? -1 : std::distance(m_layers.begin(), it);
}
// BBS
const Layer* PrintObject::get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon) const {
coordf_t limit_upper = bottom_z + epsilon;

View File

@ -110,7 +110,7 @@ SlicingParameters SlicingParameters::create_from_config(
params.min_layer_height = std::min(params.min_layer_height, params.layer_height);
params.max_layer_height = std::max(params.max_layer_height, params.layer_height);
if (! soluble_interface) {
if (! soluble_interface || is_tree_slim(object_config.support_type.value, object_config.support_style.value)) {
params.gap_raft_object = object_config.raft_contact_distance.value;
//BBS
params.gap_object_support = object_config.support_bottom_z_distance.value;

View File

@ -790,6 +790,7 @@ public:
m_extrusion_width(params.extrusion_width),
m_support_material_closing_radius(params.support_closing_radius)
{
if (m_style == smsDefault) m_style = smsGrid;
switch (m_style) {
case smsGrid:
{
@ -1578,9 +1579,8 @@ static inline Polygons detect_overhangs(
// Offset the support regions back to a full overhang, restrict them to the full overhang.
// This is done to increase size of the supporting columns below, as they are calculated by
// propagating these contact surfaces downwards.
diff_polygons =
expand(diff(intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), lower_layer_polygons),
xy_expansion, SUPPORT_SURFACES_OFFSET_PARAMETERS);
diff_polygons = diff(intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), lower_layer_polygons);
if (xy_expansion != 0) { diff_polygons = expand(diff_polygons, xy_expansion, SUPPORT_SURFACES_OFFSET_PARAMETERS); }
}
//FIXME add user defined filtering here based on minimal area or minimum radius or whatever.
@ -2191,7 +2191,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// BBS: tree support is selected so normal supports need not be generated.
// Note we still need to go through the following steps if support is disabled but raft is enabled.
if (m_object_config->enable_support.value && (m_object_config->support_type.value == stTreeAuto || m_object_config->support_type.value == stTree || m_object_config->support_type.value == stHybridAuto)) {
if (m_object_config->enable_support.value && (m_object_config->support_type.value != stNormalAuto && m_object_config->support_type.value != stNormal)) {
return MyLayersPtr();
}

View File

@ -15,10 +15,8 @@
#define _L(s) Slic3r::I18N::translate(s)
#define MAX_BRANCH_RADIUS 10.0
#define USE_PLAN_LAYER_HEIGHTS 1
#define HEIGHT_TO_SWITCH_INFILL_DIRECTION 30 // change infill direction every 20mm
#define DO_NOT_MOVER_UNDER_MM 5 // do not move contact points under 5mm
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
@ -36,7 +34,6 @@ namespace Slic3r
#define unscale_(val) ((val) * SCALING_FACTOR)
#define FIRST_LAYER_EXPANSION 1.2
static constexpr float tree_support_branch_diameter_angle = 5.0;
inline unsigned int round_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer
{
@ -201,7 +198,7 @@ static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag
static void draw_contours_and_nodes_to_svg
(
int layer_nr,
std::string layer_nr_or_z,
const ExPolygons &overhangs,
const ExPolygons &overhangs_after_offset,
const ExPolygons &outlines_below,
@ -224,8 +221,8 @@ static void draw_contours_and_nodes_to_svg
bbox.max.y() = std::max(bbox.max.y(), (coord_t)scale_(10));
SVG svg;
if(layer_nr>=0)
svg.open(get_svg_filename(std::to_string(layer_nr), name_prefix), bbox);
if(!layer_nr_or_z.empty())
svg.open(get_svg_filename(layer_nr_or_z, name_prefix), bbox);
else
svg.open(name_prefix, bbox);
if (!svg.is_opened()) return;
@ -265,8 +262,7 @@ static void draw_contours_and_nodes_to_svg
}
static void draw_layer_mst
(
int layer_nr,
(const std::string &layer_nr_or_z,
const std::vector<MinimumSpanningTree> &spanning_trees,
const ExPolygons& outline
)
@ -279,11 +275,13 @@ static void draw_layer_mst
bbox.merge(bb);
}
SVG svg(get_svg_filename(std::to_string(layer_nr), "mstree").c_str(), bbox);
SVG svg(get_svg_filename(layer_nr_or_z, "mstree").c_str(), bbox);
if (!svg.is_opened()) return;
svg.draw(lines, "blue", coord_t(scale_(0.05)));
svg.draw_outline(outline, "yellow");
for (auto &spanning_tree : spanning_trees)
svg.draw(spanning_tree.vertices(), "black", coord_t(scale_(0.1)));
}
static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2)
@ -697,6 +695,10 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
m_object_config->support_interface_pattern == smipConcentric ?
ipConcentric :
(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width;
is_slim = is_tree_slim(m_object_config->support_type, m_object_config->support_style);
MAX_BRANCH_RADIUS = is_slim ? 5.0 : 10.0;
tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0;
}
@ -713,6 +715,7 @@ void TreeSupport::detect_object_overhangs()
create_tree_support_layers();
m_ts_data = m_object->alloc_tree_support_preview_cache();
m_ts_data->is_slim = is_slim;
const PrintObjectConfig& config = m_object->config();
SupportType stype = config.support_type.value;
@ -729,7 +732,6 @@ void TreeSupport::detect_object_overhangs()
// a region is considered well supported if the number of layers below it exceeds this threshold
const int thresh_layers_below = 10 / config.layer_height;
double obj_height = m_object->size().z();
bool is_auto = (stype == stTreeAuto || stype == stHybridAuto);
struct ExPolygonComp {
bool operator()(const ExPolygon& a, const ExPolygon& b) const {
@ -822,7 +824,7 @@ void TreeSupport::detect_object_overhangs()
}
};
// main part of sharptail detections
if (std::set<SupportType>{stTreeAuto, stHybridAuto, stTree}.count(stype))// == stTreeAuto || stype == stHybridAuto || stype == stTree)
if (is_tree(stype))
{
double threshold_rad = (config.support_threshold_angle.value < EPSILON ? 30 : config.support_threshold_angle.value+1) * M_PI / 180.;
ExPolygons regions_well_supported;
@ -833,7 +835,7 @@ void TreeSupport::detect_object_overhangs()
if (m_object->print()->canceled())
break;
if (!is_auto && layer_nr > enforce_support_layers)
if (!is_auto(stype) && layer_nr > enforce_support_layers)
continue;
Layer* layer = m_object->get_layer(layer_nr);
@ -881,7 +883,7 @@ void TreeSupport::detect_object_overhangs()
ExPolygons overhangs_sharp_tail;
if (is_auto && g_config_support_sharp_tails)
if (is_auto(stype) && g_config_support_sharp_tails)
{
#if 0
// detect sharp tail and add more supports around
@ -996,13 +998,13 @@ void TreeSupport::detect_object_overhangs()
ts_layer->overhang_areas.emplace_back(poly);
}
if (is_auto && g_config_remove_small_overhangs) {
if (is_auto(stype) && g_config_remove_small_overhangs) {
for (auto& overhang : ts_layer->overhang_areas) {
find_and_insert_cluster(overhangClusters, overhang2clusterInd, overhang, layer_nr, extrusion_width_scaled);
}
}
if (is_auto && /*g_config_support_sharp_tails*/0)
if (is_auto(stype) && /*g_config_support_sharp_tails*/0)
{ // update well supported regions
ExPolygons regions_well_supported2;
// regions intersects with lower regions_well_supported or large support are also well supported
@ -1074,7 +1076,7 @@ void TreeSupport::detect_object_overhangs()
}
}
if (is_auto && g_config_remove_small_overhangs) {
if (is_auto(stype) && g_config_remove_small_overhangs) {
if (blockers.size() < m_object->layer_count())
blockers.resize(m_object->layer_count());
for (auto& cluster : overhangClusters) {
@ -1393,7 +1395,7 @@ void TreeSupport::generate_toolpaths()
{
const PrintConfig &print_config = m_object->print()->config();
const PrintObjectConfig &object_config = m_object->config();
coordf_t support_extrusion_width = object_config.support_line_width.value > 0 ? object_config.support_line_width : object_config.line_width;
coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
coordf_t nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_filament - 1);
coordf_t layer_height = object_config.layer_height.value;
@ -1862,8 +1864,7 @@ Polygons TreeSupport::contact_nodes_to_polygon(const std::vector<Node*>& contact
void TreeSupport::generate_support_areas()
{
const PrintObjectConfig &config = m_object->config();
bool tree_support_enable = config.enable_support.value &&
(config.support_type.value == stTreeAuto || config.support_type.value == stTree || config.support_type.value == stHybridAuto);
bool tree_support_enable = config.enable_support.value && is_tree(config.support_type.value);
if (!tree_support_enable)
return;
@ -1877,8 +1878,6 @@ void TreeSupport::generate_support_areas()
detect_object_overhangs();
profiler.stage_finish(STAGE_DETECT_OVERHANGS);
if (!has_overhangs) return;
// Generate contact points of tree support
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
m_object->print()->set_status(56, _L("Support: generate contact points"));
@ -1891,8 +1890,12 @@ void TreeSupport::generate_support_areas()
drop_nodes(contact_nodes);
profiler.stage_finish(STAGE_DROP_DOWN_NODES);
// smooth_nodes(contact_nodes);
#if !USE_PLAN_LAYER_HEIGHTS
// Adjust support layer heights
// adjust_layer_heights(contact_nodes);
adjust_layer_heights(contact_nodes);
#endif
//Generate support areas.
profiler.stage_start(STAGE_DRAW_CIRCLES);
@ -1919,23 +1922,40 @@ void TreeSupport::generate_support_areas()
BOOST_LOG_TRIVIAL(debug) << "tree support time " << profiler.report();
}
inline coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor)
coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor)
{
double radius;
#if 1
if ((layers_to_top + 1) > tip_layers)
if (!is_slim) {
if ((layers_to_top + 1) > tip_layers) {
radius = base_radius + base_radius * (layers_to_top + 1) * diameter_angle_scale_factor;
} else {
radius = base_radius * (layers_to_top + 1) / tip_layers;
}
} else {
if ((layers_to_top + 1) > tip_layers * 2) {
radius = base_radius + base_radius * (layers_to_top + 1) * diameter_angle_scale_factor;
} else {
radius = base_radius * (layers_to_top + 1) / (tip_layers * 2);
}
radius = std::max(radius, MIN_BRANCH_RADIUS);
}
radius = std::min(radius, MAX_BRANCH_RADIUS);
return radius;
}
coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor)
{
double radius;
if (mm_to_top > base_radius)
{
radius = base_radius + base_radius * (layers_to_top + 1) * diameter_angle_scale_factor;
radius = base_radius + mm_to_top * diameter_angle_scale_factor;
}
else
{
radius = base_radius * (layers_to_top + 1) / tip_layers;
radius = mm_to_top * diameter_angle_scale_factor;
}
#else
double scale = static_cast<double>(layers_to_top + 1) / tip_layers;
scale = layers_to_top < tip_layers ? (0.5 + scale / 2) : (1 + static_cast<double>(layers_to_top - tip_layers) * diameter_angle_scale_factor);
radius = scale * base_radius;
#endif
radius = std::max(radius, MIN_BRANCH_RADIUS);
radius = std::min(radius, MAX_BRANCH_RADIUS);
return radius;
}
@ -1999,12 +2019,12 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
const size_t top_interface_layers = config.support_interface_top_layers.value;
const size_t bottom_interface_layers = config.support_interface_bottom_layers.value;
const size_t tip_layers = branch_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle.
const double diameter_angle_scale_factor = sin(tree_support_branch_diameter_angle * M_PI / 180.) * layer_height / branch_radius; //Scale factor per layer to produce the desired angle.
const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.);// * layer_height / branch_radius; //Scale factor per layer to produce the desired angle.
const coordf_t line_width = config.support_line_width;
const coordf_t line_width_scaled = scale_(line_width);
const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning;
coordf_t support_extrusion_width = config.support_line_width.value > 0 ? config.support_line_width : config.line_width;
coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
const size_t wall_count = config.tree_support_wall_count.value;
const PrintObjectConfig& object_config = m_object->config();
@ -2059,18 +2079,15 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
break;
const Node& node = *p_node;
ExPolygon area;
ExPolygons area;
// Generate directly from overhang polygon if one of the following is true:
// 1) node is a normal part of hybrid support
// 2) top interface layers are enabled
// 3) node is virtual
if (node.type == ePolygon || (top_interface_layers>0 &&node.support_roof_layers_below > 0) || node.distance_to_top<0) {
// 2) node is virtual
if (node.type == ePolygon || node.distance_to_top<0) {
if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1)
area = *node.overhang;
area.emplace_back(*node.overhang);
else {
auto tmp = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance));
if(!tmp.empty()) // can be empty for non-manifold models
area = tmp[0];
area = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance));
}
}
else {
@ -2080,43 +2097,65 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
if (top_interface_layers>0) { // if has interface, branch circles should be larger
scale = static_cast<double>(layers_to_top + 1) / tip_layers;
scale = layers_to_top < tip_layers ? (0.5 + scale / 2) : (1 + static_cast<double>(layers_to_top - tip_layers) * diameter_angle_scale_factor);
} else { // directly calc scale from the radius used in drop_nodes
scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius;
}
if (is_slim && 0) {
double moveX = node.movement.x() / (scale * branch_radius_scaled);
double moveY = node.movement.y() / (scale * branch_radius_scaled);
const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY));
double matrix[2*2] = {
scale * (1 + moveX * moveX * vsize_inv),scale * (0 + moveX * moveY * vsize_inv),
scale * (0 + moveX * moveY * vsize_inv),scale * (1 + moveY * moveY * vsize_inv),
};
for (auto vertex: branch_circle.points) {
vertex = Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y());
circle.append(node.position + vertex);
}
} else {
scale = calc_branch_radius(1, node.distance_to_top, tip_layers, diameter_angle_scale_factor);
scale = std::min(scale, MAX_BRANCH_RADIUS / branch_radius);
for (auto iter = branch_circle.points.begin(); iter != branch_circle.points.end(); iter++) {
Point corner = (*iter) * scale;
circle.append(node.position + corner);
}
}
for (auto iter = branch_circle.points.begin(); iter != branch_circle.points.end(); iter++)
{
Point corner = (*iter) * scale;
circle.append(node.position + corner);
}
if (layer_nr == 0 && m_raft_layers == 0) {
double brim_width = layers_to_top * layer_height / (scale * branch_radius) * 0.5;
circle = offset(circle, scale_(brim_width))[0];
}
area = ExPolygon(circle);
area.emplace_back(ExPolygon(circle));
// merge overhang to get a smoother interface surface
if (top_interface_layers > 0 && node.support_roof_layers_below > 0) {
ExPolygons overhang_expanded;
if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1)
overhang_expanded.emplace_back(*node.overhang);
else {
// 对于有缺陷的模型overhang膨胀以后可能是空的
overhang_expanded = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance));
}
append(area, overhang_expanded);
}
}
if (node.distance_to_top < 0)
roof_gap_areas.emplace_back(area);
append(roof_gap_areas, area);
else if (node.support_roof_layers_below == 1)
{
roof_1st_layer.emplace_back(area);
append(roof_1st_layer, area);
max_layers_above_roof1 = std::max(max_layers_above_roof1, node.distance_to_top);
}
else if (node.support_roof_layers_below > 0)
{
roof_areas.emplace_back(area);
append(roof_areas, area);
max_layers_above_roof = std::max(max_layers_above_roof, node.distance_to_top);
}
else
{
base_areas.emplace_back(area);
append(base_areas, area);
max_layers_above_base = std::max(max_layers_above_base, node.distance_to_top);
}
if (layer_nr < brim_skirt_layers)
ts_layer->lslices.emplace_back(area);
append(ts_layer->lslices, area);
}
ts_layer->lslices = std::move(union_ex(ts_layer->lslices));
@ -2131,7 +2170,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
m_object->print()->set_status(65, (boost::format( _L("Support: generate polygons at layer %d")) % layer_nr).str());
// join roof segments
double contact_dist_scaled = scale_(m_slicing_params.gap_support_object);
double contact_dist_scaled = scale_(0.5);// scale_(m_slicing_params.gap_support_object);
roof_areas = std::move(offset2_ex(roof_areas, contact_dist_scaled, -contact_dist_scaled));
roof_1st_layer = std::move(offset2_ex(roof_1st_layer, contact_dist_scaled, -contact_dist_scaled));
@ -2180,9 +2219,9 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
}
if (bottom_gap_layers > 0 && layer_nr > bottom_gap_layers) {
const Layer* below_layer = m_object->get_layer(layer_nr - bottom_gap_layers);
ExPolygons bottom_gap = std::move(intersection_ex(floor_areas, below_layer->lslices));
if (!bottom_gap.empty()) {
floor_areas = std::move(diff_ex(floor_areas, bottom_gap));
ExPolygons bottom_gap_area = std::move(intersection_ex(floor_areas, below_layer->lslices));
if (!bottom_gap_area.empty()) {
floor_areas = std::move(diff_ex(floor_areas, bottom_gap_area));
}
}
auto &area_groups = ts_layer->area_groups;
@ -2397,7 +2436,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
ExPolygons& floor_areas = ts_layer->floor_areas;
if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue;
char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z);
draw_contours_and_nodes_to_svg(-1, base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), { "base", "roof", "roof1st" });
draw_contours_and_nodes_to_svg("", base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), {"base", "roof", "roof1st"});
}
// export layer & print_z log
@ -2429,25 +2468,51 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
{
const PrintObjectConfig &config = m_object->config();
// Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down.
const coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
const coordf_t layer_height = config.layer_height.value;
const double angle = config.tree_support_branch_angle.value * M_PI / 180.;
const int wall_count = std::max(1, config.tree_support_wall_count.value);
const double tan_angle = tan(angle);
double tan_angle = tan(angle); // when nodes are thick, they can move further. this is the max angle
const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits<coordf_t>::max();
const double max_move_distance2 = max_move_distance * max_move_distance;
const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
const size_t tip_layers = branch_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle.
const double diameter_angle_scale_factor = sin(tree_support_branch_diameter_angle * M_PI / 180.) * layer_height / branch_radius; //Scale factor per layer to produce the desired angle.
const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.);//*layer_height / branch_radius; // Scale factor per layer to produce the desired angle.
const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution;
const bool support_on_buildplate_only = config.support_on_build_plate_only.value;
const size_t bottom_interface_layers = config.support_interface_bottom_layers.value;
const size_t top_interface_layers = config.support_interface_top_layers.value;
float DO_NOT_MOVER_UNDER_MM = is_slim ? 0 : 5; // do not move contact points under 5mm
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](const Node *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);
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;
double tan_angle = tan(angle * M_PI / 180);
int wall_count_ = node->radius > 2 * config.support_line_width ? wall_count : 1;
node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) * wall_count_ : std::numeric_limits<coordf_t>::max();
node->max_move_dist = std::min(node->max_move_dist, support_extrusion_width);
move_dist = node->max_move_dist;
}
if (power == 2) move_dist = SQ(move_dist);
return move_dist;
};
std::vector<std::pair<coordf_t, coordf_t>> layer_heights = plan_layer_heights(contact_nodes);
if (layer_heights.empty()) return;
std::unordered_set<Node*> to_free_node_set;
m_spanning_trees.resize(contact_nodes.size());
//m_mst_line_x_layer_contour_caches.resize(contact_nodes.size());
if (!is_slim)
{// get outlines below and avoidance area using tbb
//m_object->print()->set_status(59, "Support: preparing avoidance regions ");
// get all the possible radiis
@ -2495,9 +2560,11 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (layer_contact_nodes.empty())
continue;
int jump_nr = 1;
while (layer_heights[layer_nr - jump_nr].second < EPSILON)
jump_nr++;
int layer_nr_next = layer_nr - 1;
while (layer_nr_next>=0 && layer_heights[layer_nr_next].second < EPSILON)
layer_nr_next--;
coordf_t print_z_next = layer_heights[layer_nr_next].first;
coordf_t height_next = layer_heights[layer_nr_next].second;
std::deque<std::pair<size_t, Node*>> 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_tree_support_layer(layer_nr);
@ -2535,10 +2602,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
const Node& node = *p_node;
if (node.distance_to_top < 0) {
// virtual node do not merge or move
// gap nodes do not merge or move
Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node,
layer_heights[layer_nr - jump_nr].first, layer_heights[layer_nr - jump_nr].second);
contact_nodes[layer_nr - jump_nr].emplace_back(next_node);
print_z_next, height_next);
get_max_move_dist(next_node);
next_node->is_merged = false;
contact_nodes[layer_nr_next].emplace_back(next_node);
continue;
}
if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported.
@ -2555,8 +2624,10 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
// polygon node do not merge or move
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position);
Node *next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, to_buildplate, p_node,
layer_heights[layer_nr - jump_nr].first, layer_heights[layer_nr - jump_nr].second);
contact_nodes[layer_nr - jump_nr].emplace_back(next_node);
print_z_next, height_next);
next_node->max_move_dist = 0;
next_node->is_merged = false;
contact_nodes[layer_nr_next].emplace_back(next_node);
continue;
}
/* Find which part this node is located in and group the nodes in
@ -2610,7 +2681,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
coordf_t branch_radius_temp = 0;
coordf_t max_y = std::numeric_limits<coordf_t>::min();
draw_layer_mst(layer_nr, spanning_trees, m_object->get_layer(layer_nr)->lslices);
draw_layer_mst(std::to_string(layer_nr), spanning_trees, m_object->get_layer(layer_nr)->lslices);
#endif
for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++)
{
@ -2631,9 +2702,9 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
//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.distance_to_top, tip_layers, diameter_angle_scale_factor);
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor);
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr - jump_nr);
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next);
if (group_index == 0)
{
//Avoid collisions.
@ -2644,12 +2715,15 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
Node* neighbour = nodes_per_part[group_index][neighbours[0]];
size_t new_distance_to_top = std::max(node.distance_to_top, neighbour->distance_to_top) + 1;
size_t new_support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour->support_roof_layers_below) - 1;
double new_dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour->dist_mm_to_top) + node.height;
const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr - jump_nr), next_position);
const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position);
Node * next_node = new Node(next_position, new_distance_to_top, node.skin_direction, new_support_roof_layers_below, to_buildplate, p_node,
layer_heights[layer_nr - jump_nr].first, layer_heights[layer_nr - jump_nr].second);
layer_heights[layer_nr_next].first, layer_heights[layer_nr_next].second, new_dist_mm_to_top);
next_node->movement = next_position - node.position;
contact_nodes[layer_nr - jump_nr].push_back(next_node);
get_max_move_dist(next_node);
next_node->is_merged = true;
contact_nodes[layer_nr_next].push_back(next_node);
// Make sure the next pass doesn't drop down either of these (since that already happened).
node.merged_neighbours.push_front(neighbour);
@ -2661,13 +2735,15 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
//Remove all neighbours that are too close and merge them into this node.
for (const Point& neighbour : neighbours)
{
if (vsize2_with_unscale(neighbour - node.position) < max_move_distance2)
if (vsize2_with_unscale(neighbour - node.position) < /*max_move_distance2*/get_max_move_dist(&node,2))
{
Node* neighbour_node = nodes_per_part[group_index][neighbour];
node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top);
node.support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour_node->support_roof_layers_below);
node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top);
node.merged_neighbours.push_front(neighbour_node);
node.merged_neighbours.insert_after(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
node.is_merged = true;
to_delete.insert(neighbour_node);
}
}
@ -2686,7 +2762,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
//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(m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr), node.position))
{
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.distance_to_top, tip_layers, diameter_angle_scale_factor);
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor);
Point to_outside = projection_onto_ex(m_ts_data->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.
@ -2715,110 +2791,119 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
}
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);
// 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);
float max_dist_to_move = 10.0*tan_angle; // don't move if moving down by 10mm and they still can't merge
if (ts_layer->print_z > DO_NOT_MOVER_UNDER_MM &&
(neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) //Only nodes that aren't about to collapse.
(neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) // Only nodes that aren't about to collapse.
{
//Move towards the average position of all neighbours.
// Move towards the average position of all neighbours.
Point sum_direction(0, 0);
for (const Point& neighbour : neighbours)
{
// do not move to neighbor that's too far away
float dist2_to_neighbor = vsize2_with_unscale(neighbour - node.position);
if (dist2_to_neighbor > SQ(max_dist_to_move)) continue;
for (const Point &neighbour : neighbours) {
// do not move to the neighbor to be deleted
Node *neighbour_node = nodes_per_part[group_index][neighbour];
if (to_delete.find(neighbour_node) != to_delete.end()) continue;
Point direction = neighbour - node.position;
Node *neighbour_node = nodes_per_part[group_index][neighbour];
coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.distance_to_top + 1, tip_layers, diameter_angle_scale_factor);
coordf_t neighbour_bottom_radius = calc_branch_radius(branch_radius, neighbour_node->distance_to_top + 1, tip_layers, diameter_angle_scale_factor);
const coordf_t min_overlap = branch_radius;
double max_converge_distance = tan_angle * (ts_layer->print_z - DO_NOT_MOVER_UNDER_MM) + branch_bottom_radius + neighbour_bottom_radius - min_overlap;
if (vsize2_with_unscale(direction) > max_converge_distance * max_converge_distance)
continue;
// do not move to neighbor that's too far away (即使以最大速度移动,在接触热床之前都无法汇聚)
float dist2_to_neighbor = vsize2_with_unscale(direction);
if (is_line_cut_by_contour(node.position, neighbour))
continue;
coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + node.print_z, diameter_angle_scale_factor);
coordf_t neighbour_bottom_radius = calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top + neighbour_node->print_z, diameter_angle_scale_factor);
double max_converge_distance = tan_angle * (ts_layer->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius);
if (dist2_to_neighbor > max_converge_distance * max_converge_distance) continue;
sum_direction += direction;
if (is_line_cut_by_contour(node.position, neighbour)) continue;
if (is_slim)
sum_direction += direction * (1 / dist2_to_neighbor);
else
sum_direction += direction;
}
if(vsize2_with_unscale(sum_direction) <= max_move_distance2)
{
if (is_slim)
move_to_neighbor_center = sum_direction;
}
else
{
move_to_neighbor_center = normal(sum_direction, scale_(max_move_distance));
}
// add momentum to force smooth movement
move_to_neighbor_center = move_to_neighbor_center * 0.5 + p_node->movement * 0.5;
}
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.distance_to_top, tip_layers, 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;
}
#endif
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr - jump_nr);
Point to_outside = projection_onto_ex(avoid_layer, node.position);
Point movement = to_outside - node.position;
double movelength2 = vsize2_with_unscale(movement);
// don't move if
// 1) line of node and to_outside is cut by contour (means supports may intersect with object)
// 2) it's impossible to move to build plate
if (is_line_cut_by_contour(node.position, to_outside) || movelength2 > max_move_distance2 * SQ(layer_nr))
movement = Point(0, 0);
else if (movelength2 > max_move_distance2) {
if (is_inside_ex(avoid_layer, node.position))
movement = normal(movement, scale_(max_move_distance));
else
movement = Point(0, 0); // point is already outside contour, no need to move
}
// move to the averaged direction of neighbor center and contour edge if they are roughly same direction
if (movement.dot(move_to_neighbor_center) >= 0)
movement = movement + move_to_neighbor_center;
// Cant do this. Otherwise we'll get a lot of supports in-the-air (nodes terminated too early)
else
movement = move_to_neighbor_center; // otherwise move to neighbor center first
if (vsize2_with_unscale(movement) > max_move_distance2)
movement = normal(movement, scale_(max_move_distance));
next_layer_vertex += movement;
if (/*group_index ==*/ 0)
{
//Avoid collisions.
const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; //100 micron extra for rounding errors.
bool is_outside = move_out_expolys(avoid_layer, next_layer_vertex, radius_sample_resolution + EPSILON, max_move_between_samples);
if (!is_outside) {
Point candidate_vertex = node.position;
is_outside = move_out_expolys(avoid_layer, candidate_vertex, radius_sample_resolution + EPSILON, max_move_between_samples);
if (is_outside) {
next_layer_vertex = candidate_vertex;
else {
if (vsize2_with_unscale(sum_direction) <= max_move_distance2) {
move_to_neighbor_center = sum_direction;
} else {
move_to_neighbor_center = normal(sum_direction, scale_(get_max_move_dist(&node)));
}
}
}
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;
}
#endif
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next);
Point to_outside = projection_onto_ex(avoid_layer, node.position);
Point direction_to_outer = to_outside - node.position;
double dist2_to_outer = vsize2_with_unscale(direction_to_outer);
// don't move if
// 1) line of node and to_outside is cut by contour (means supports may intersect with object)
// 2) it's impossible to move to build plate
if (is_line_cut_by_contour(node.position, to_outside) || dist2_to_outer > max_move_distance2 * SQ(layer_nr) ||
!is_inside_ex(avoid_layer, node.position)) {
// try move to outside of lower layer instead
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.
bool is_outside = move_out_expolys(avoid_layer, 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);
} else {
direction_to_outer = Point(0, 0);
dist2_to_outer = 0;
}
}
// move to the averaged direction of neighbor center and contour edge if they are roughly same direction
Point movement;
if (is_slim)
movement = move_to_neighbor_center*2 + (dist2_to_outer > EPSILON ? direction_to_outer * (1 / dist2_to_outer) : Point(0, 0));
else {
if (movement.dot(move_to_neighbor_center) >= 0.2 || move_to_neighbor_center == Point(0, 0))
movement = direction_to_outer + move_to_neighbor_center;
else
movement = move_to_neighbor_center; // otherwise move to neighbor center first
}
if (vsize2_with_unscale(movement) > get_max_move_dist(&node,2))
movement = normal(movement, scale_(get_max_move_dist(&node)));
// add momentum to force smooth movement
//movement = movement * 0.5 + p_node->movement * 0.5;
next_layer_vertex += movement;
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex);
Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, node.skin_direction, node.support_roof_layers_below - 1, to_buildplate, p_node,
layer_heights[layer_nr - jump_nr].first, layer_heights[layer_nr - jump_nr].second);
print_z_next, height_next);
next_node->movement = movement;
contact_nodes[layer_nr - jump_nr].push_back(next_node);
get_max_move_dist(next_node);
next_node->is_merged = false;
contact_nodes[layer_nr_next].push_back(next_node);
}
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
draw_contours_and_nodes_to_svg(layer_nr, m_ts_data->get_avoidance(0, layer_nr), m_ts_data->get_avoidance(branch_radius_temp, layer_nr), m_ts_data->m_layer_outlines_below[layer_nr],
contact_nodes[layer_nr], contact_nodes[layer_nr - jump_nr], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" });
draw_contours_and_nodes_to_svg(std::to_string(ts_layer->print_z), m_ts_data->get_avoidance(0, layer_nr), m_ts_data->get_avoidance(branch_radius_temp, layer_nr), m_ts_data->m_layer_outlines_below[layer_nr],
contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" });
if (contact_nodes[layer_nr].empty() == false) {
BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z;
for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) {
auto &node = contact_nodes[layer_nr][i];
BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement << ", is_merged=" << node->is_merged;
}
}
#endif
// Prune all branches that couldn't find support on either the model or the buildplate (resulting in 'mid-air' branches).
@ -2888,6 +2973,66 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
#endif
}
void TreeSupport::smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes)
{
std::map<Node *, bool> is_processed;
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr];
if (curr_layer_nodes.empty()) continue;
for (Node *node : curr_layer_nodes) {
is_processed.emplace(node, false);
if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes
}
}
for (int layer_nr = contact_nodes.size()-1; layer_nr >=0 ; layer_nr--) {
std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr];
if (curr_layer_nodes.empty()) continue;
for (Node *node : curr_layer_nodes) {
if (!is_processed[node]) {
std::vector<Point> pts;
std::vector<Node *> branch;
Node * p_node = node;
// add head for second path from the merged node if there are multiple ones
if (!node->is_merged && node->parent) {
pts.push_back(p_node->parent->position);
branch.push_back(p_node->parent);
}
do {
pts.push_back(p_node->position);
//is_processed[p_node] = true;
branch.push_back(p_node);
p_node = p_node->child;
} while (p_node && !p_node->is_merged && !is_processed[p_node]);
// TODO is it necessary to add tail also?
if (p_node && p_node->is_merged) {
pts.push_back(p_node->position);
branch.push_back(p_node);
}
if (pts.size() < 3) continue;
std::vector<Point> pts1 = pts;
// TODO here we assume layer height gap is constant. If not true, need to consider height jump
for (size_t k = 0; k < 2; 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[i2] + pts[i - 1] + pts[i] + pts[i + 1] + pts[i3]) / 5;
pts1[i] = pt;
}
std::swap(pts, pts1);
}
for (size_t i = 1; i < pts.size() - 1; i++) {
if (!is_processed[branch[i]]) {
branch[i]->position = pts[i];
is_processed[branch[i]] = true;
}
}
}
}
}
}
void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes)
{
if (contact_nodes.empty())
@ -2895,8 +3040,9 @@ void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_
const PrintConfig& print_config = m_object->print()->config();
const PrintObjectConfig& config = m_object->config();
if (!print_config.independent_support_layer_height) {
// don't merge layers for Vine support, or the branches will be unsmooth
// TODO can we merge layers in a way that guaranttees smoothness?
if (!print_config.independent_support_layer_height || is_slim) {
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
for (Node* node : curr_layer_nodes) {
@ -2995,8 +3141,13 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
std::vector<std::pair<coordf_t, coordf_t>> layer_heights(contact_nodes.size(), std::pair<coordf_t, coordf_t>(0.0, 0.0));
std::vector<int> bounds;
if (layer_height == max_layer_height)
return std::vector<std::pair<coordf_t, coordf_t>>();
if (!USE_PLAN_LAYER_HEIGHTS || layer_height == max_layer_height) {
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
layer_heights[layer_nr].first = m_object->get_layer(layer_nr)->print_z;
layer_heights[layer_nr].second = m_object->get_layer(layer_nr)->height;
}
return layer_heights;
}
bounds.push_back(0);
// Keep first layer still
@ -3148,11 +3299,12 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
for (const ExPolygon &overhang_part : overhang)
{
BoundingBox overhang_bounds = get_extents(overhang_part);
if (config.support_type.value==stHybridAuto && overhang_part.area() > thresh_big_overhang) {
if (config.support_style.value==smsTreeHybrid && overhang_part.area() > thresh_big_overhang) {
Point candidate = overhang_bounds.center();
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
Node *contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers+ z_distance_top_layers, true, Node::NO_PARENT, print_z, height);
Node *contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
height, z_distance_top);
contact_node->type = ePolygon;
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
@ -3179,7 +3331,8 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
//if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate))
{
constexpr bool to_buildplate = true;
Node* contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers+ z_distance_top_layers, to_buildplate, Node::NO_PARENT,print_z,height);
Node * contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers + z_distance_top_layers, to_buildplate,
Node::NO_PARENT, print_z, height, z_distance_top);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
added = true;
@ -3201,7 +3354,8 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
constexpr bool to_buildplate = true;
Node * contact_node = new Node(candidate, -z_distance_top_layers, layer_nr % 2, support_roof_layers+ z_distance_top_layers, to_buildplate, Node::NO_PARENT, print_z, height);
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr % 2, support_roof_layers + z_distance_top_layers, to_buildplate, Node::NO_PARENT,
print_z, height, z_distance_top);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
}
@ -3214,12 +3368,14 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
auto v1 = (pt - points[(i - 1 + points.size()) % points.size()]).normalized();
auto v2 = (pt - points[(i + 1) % points.size()]).normalized();
if (v1.dot(v2) > -0.7) {
Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr % 2, support_roof_layers+ z_distance_top_layers, true, Node::NO_PARENT, print_z, height);
Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr % 2, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
height, z_distance_top);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
}
}
} else if(ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Enforced){
}
if(ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Enforced || is_slim){
// remove close points in Enforcers
auto above_nodes = contact_nodes[layer_nr - 1];
if (!curr_nodes.empty() && !above_nodes.empty()) {
@ -3243,7 +3399,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
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(layer_nr, overhang, m_ts_data->m_layer_outlines_below[layer_nr], {},
draw_contours_and_nodes_to_svg(std::to_string(print_z), overhang, m_ts_data->m_layer_outlines_below[layer_nr], {},
contact_nodes[layer_nr], {}, "init_contact_points", { "overhang","outlines","" });
#endif
}
@ -3387,39 +3543,37 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke
const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& key) const
{
const auto& radius = key.first;
const auto& layer_idx = key.second;
#if 0
if (layer_idx == 0)
{
m_avoidance_cache[key] = get_collision(radius, 0);
return m_avoidance_cache[key];
}
const auto& layer_nr = key.second;
std::pair<tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash>::iterator,bool> ret;
if (is_slim) {
if (layer_nr == 0) {
m_avoidance_cache[key] = get_collision(radius, 0);
return m_avoidance_cache[key];
}
// Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if
// called at high layer heights. We can limit the reqursion depth to N by checking if the layer N
// below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion
// if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers
// below our current one.
constexpr auto max_recursion_depth = 100;
// Check if we would exceed the recursion limit by trying to process this layer
if (layer_nr >= max_recursion_depth
&& m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end())
{
// Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result.
get_avoidance(radius, layer_nr - max_recursion_depth);
}
// Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if
// called at high layer heights. We can limit the reqursion depth to N by checking if the layer N
// below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion
// if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers
// below our current one.
constexpr auto max_recursion_depth = 100;
// Check if we would exceed the recursion limit by trying to process this layer
if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end()) {
// Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result.
get_avoidance(radius, layer_nr - max_recursion_depth);
}
ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move)));
const ExPolygons& collision = get_collision(radius, layer_nr);
avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end());
avoidance_areas = std::move(union_ex(avoidance_areas));
const auto ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)});
assert(ret.second);
#else
ExPolygons avoidance_areas = std::move(offset_ex(m_layer_outlines_below[layer_idx], scale_(m_xy_distance+radius)));
const auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) });
assert(ret.second);
#endif
ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move)));
const ExPolygons &collision = get_collision(radius, layer_nr);
avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end());
avoidance_areas = std::move(union_ex(avoidance_areas));
ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)});
assert(ret.second);
} else {
ExPolygons avoidance_areas = std::move(offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius)));
ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)});
assert(ret.second);
}
return ret.first->second;
}

View File

@ -121,6 +121,7 @@ private:
ExPolygon m_machine_border;
public:
bool is_slim = false;
/*!
* \brief The required clearance between the model and the tree branches
*/
@ -224,7 +225,7 @@ public:
{}
Node(const Point position, const int distance_to_top, const bool skin_direction, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_)
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
: distance_to_top(distance_to_top)
, position(position)
, skin_direction(skin_direction)
@ -234,10 +235,13 @@ public:
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
{
if (parent) {
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this;
}
}
@ -255,6 +259,7 @@ public:
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*!
* \brief The position of this node on the layer.
@ -262,8 +267,10 @@ public:
Point position;
Point movement; // movement towards neighbor center or outline
double radius;
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
NodeType type = eCircle;
bool is_merged = false; // this node is generated by merging upper nodes
const ExPolygon* overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*!
@ -311,7 +318,7 @@ public:
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::forward_list<Node*> merged_neighbours;
std::list<Node*> merged_neighbours;
coordf_t print_z;
coordf_t height;
@ -328,6 +335,7 @@ public:
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
@ -372,6 +380,11 @@ private:
size_t m_highest_overhang_layer;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
coordf_t MAX_BRANCH_RADIUS = 10.0;
coordf_t MIN_BRANCH_RADIUS = 0.5;
float tree_support_branch_diameter_angle = 5.0;
bool is_slim = false;
/*!
* \brief Draws circles around each node of the tree into the final support.
@ -397,7 +410,9 @@ private:
* dropped down. The nodes are dropped to lower layers inside the same
* vector of layers.
*/
void drop_nodes(std::vector<std::vector<Node*>>& contact_nodes);
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
@ -432,6 +447,8 @@ private:
void generate_toolpaths();
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr);
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);
};
}

View File

@ -349,6 +349,30 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
is_msg_dlg_already_exist = false;
}
// BBS
if (config->opt_bool("enable_support") && is_tree(config->opt_enum<SupportType>("support_type"))
&& (config->opt_enum<SupportMaterialStyle>("support_style")==smsDefault || config->opt_enum<SupportMaterialStyle>("support_style")==smsTreeSlim)
&& !(config->opt_float("support_top_z_distance")==0 && config->opt_int("support_interface_top_layers")==0 && config->opt_int("tree_support_wall_count")==2))
{
wxString msg_text = _(L("We have added an experimental style \"Tree Slim\" that features smaller support volume but weaker strength.\n"
"We recommand using it with: 0 interface layers, 0 top distance, 2 walls.\n"));
if (is_global_config)
msg_text += "\n" + _(L("Change these settings automatically? \n"
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me"));
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true;
auto answer = dialog.ShowModal();
if (!is_global_config || answer == wxID_YES) {
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0));
new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(0));
new_conf.set_key_value("tree_support_wall_count", new ConfigOptionInt(2));
}
apply(config, &new_conf);
is_msg_dlg_already_exist = false;
}
// BBS
int filament_cnt = wxGetApp().preset_bundle->filament_presets.size();
#if 0
@ -571,19 +595,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"support_type", "support_on_build_plate_only", "support_critical_regions_only",
"support_object_xy_distance", "independent_support_layer_height"})
toggle_field(el, have_support_material);
toggle_field("support_threshold_angle", have_support_material && (support_type == stNormalAuto || support_type == stTreeAuto || support_type==stHybridAuto));
toggle_field("support_threshold_angle", have_support_material && is_auto(support_type));
//toggle_field("support_closing_radius", have_support_material && support_style == smsSnug);
bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type);
for (auto el : {"tree_support_branch_angle", "tree_support_wall_count", "tree_support_branch_distance", "tree_support_branch_diameter"})
toggle_field(el, config->opt_bool("enable_support") && (support_type == stTreeAuto || support_type == stTree || support_type == stHybridAuto));
toggle_field(el, support_is_tree);
// hide tree support settings when normal is selected
bool support_is_tree = std::set<SupportType>{stTreeAuto, stHybridAuto, stTree}.count(support_type) != 0;
toggle_line("tree_support_branch_distance", support_is_tree);
toggle_line("tree_support_branch_diameter", support_is_tree);
toggle_line("tree_support_branch_angle", support_is_tree);
toggle_line("tree_support_wall_count", support_is_tree);
toggle_line("max_bridge_length", support_is_tree);
for (auto el : {"tree_support_branch_angle", "tree_support_wall_count", "tree_support_branch_distance", "tree_support_branch_diameter", "max_bridge_length"})
toggle_line(el, support_is_tree);
// tree support use max_bridge_length instead of bridge_no_support
toggle_line("bridge_no_support", !support_is_tree);

View File

@ -78,7 +78,7 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::OBJECT_C
}},
{ L("Support"), {{"brim_type", "",1},{"brim_width", "",2},{"brim_object_gap", "",3},
{"enable_support", "",4},{"support_type", "",5},{"support_threshold_angle", "",6},{"support_on_build_plate_only", "",7},
{"support_filament", "",8},{"support_interface_filament", "",9},{"support_expansion", "",24},
{"support_filament", "",8},{"support_interface_filament", "",9},{"support_expansion", "",24},{"support_style", "",25},
{"tree_support_branch_angle", "",10}, {"tree_support_wall_count", "",11},//tree support
{"support_top_z_distance", "",13},{"support_bottom_z_distance", "",12},{"support_base_pattern", "",14},{"support_base_pattern_spacing", "",15},
{"support_interface_top_layers", "",16},{"support_interface_bottom_layers", "",17},{"support_interface_spacing", "",18},{"support_bottom_interface_spacing", "",19},

View File

@ -540,10 +540,7 @@ int GLGizmoFdmSupports::get_selection_support_threshold_angle()
SupportType support_type = obj_cfg.option("support_type") ? obj_cfg.opt_enum<SupportType>("support_type") : glb_cfg.opt_enum<SupportType>("support_type");
int support_threshold_angle = obj_cfg.option("support_threshold_angle") ? obj_cfg.opt_int("support_threshold_angle") : glb_cfg.opt_int("support_threshold_angle");
bool auto_support = enable_support &&
(support_type == SupportType::stHybridAuto ||
support_type == SupportType::stTreeAuto ||
support_type == SupportType::stNormalAuto);
bool auto_support = enable_support && is_auto(support_type);
return auto_support ? support_threshold_angle : 0;
}
@ -725,8 +722,7 @@ void GLGizmoFdmSupports::init_print_instance()
const PrintObjectConfig& config = m_print_instance.print_object->config();
m_angle_threshold_deg = config.support_angle;
m_is_tree_support = config.enable_support.value &&
(config.support_type.value == stTreeAuto || config.support_type.value == stTree || config.support_type.value==stHybridAuto);
m_is_tree_support = config.enable_support.value && is_tree(config.support_type.value);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",get support_angle "<< m_angle_threshold_deg<<", is_tree "<<m_is_tree_support;
return;

View File

@ -1874,6 +1874,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Support"), L"param_support");
optgroup->append_single_option_line("enable_support", "support");
optgroup->append_single_option_line("support_type", "support#support-types");
optgroup->append_single_option_line("support_style", "support#support-styles");
optgroup->append_single_option_line("support_threshold_angle", "support#threshold-angle");
optgroup->append_single_option_line("support_on_build_plate_only");
optgroup->append_single_option_line("support_critical_regions_only");
@ -1889,7 +1890,6 @@ void TabPrint::build()
optgroup->append_single_option_line("support_interface_filament", "support#support-filament");
//optgroup = page->new_optgroup(L("Options for support material and raft"));
//optgroup->append_single_option_line("support_style");
//BBS
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");