diff --git a/resources/profiles/BBL/process/fdm_process_bbl_common.json b/resources/profiles/BBL/process/fdm_process_bbl_common.json index 091181418..9bc03d927 100644 --- a/resources/profiles/BBL/process/fdm_process_bbl_common.json +++ b/resources/profiles/BBL/process/fdm_process_bbl_common.json @@ -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", diff --git a/resources/profiles/Creality/process/fdm_process_creality_common.json b/resources/profiles/Creality/process/fdm_process_creality_common.json index 5b900ab1b..cd070cb22 100644 --- a/resources/profiles/Creality/process/fdm_process_creality_common.json +++ b/resources/profiles/Creality/process/fdm_process_creality_common.json @@ -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", diff --git a/resources/profiles/Voron/process/fdm_process_voron_common.json b/resources/profiles/Voron/process/fdm_process_voron_common.json index 740ab8e05..758be9d99 100644 --- a/resources/profiles/Voron/process/fdm_process_voron_common.json +++ b/resources/profiles/Voron/process/fdm_process_voron_common.json @@ -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", diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index a64b3b878..82ad753be 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -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(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 429d44b84..8c6339a8d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -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); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 852d48165..62c43db03 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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::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::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(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(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 diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f88bf0a2e..b9569b758 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -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{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{stNormalAuto, stTreeAuto}.count(stype) != 0; }; enum SeamPosition { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 05330140d..4cc8b2fc2 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -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; diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 36c04a523..53f70a81d 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -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; diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 75d8c0052..50c834bf5 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -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(); } diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index ce8d5ffc8..39b8f3ae8 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -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 &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{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& 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(layers_to_top + 1) / tip_layers; - scale = layers_to_top < tip_layers ? (0.5 + scale / 2) : (1 + static_cast(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>& 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>& 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>& contact_no if (top_interface_layers>0) { // if has interface, branch circles should be larger scale = static_cast(layers_to_top + 1) / tip_layers; scale = layers_to_top < tip_layers ? (0.5 + scale / 2) : (1 + static_cast(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>& 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>& 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>& 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>& 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::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::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> layer_heights = plan_layer_heights(contact_nodes); + if (layer_heights.empty()) return; std::unordered_set 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>& 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> 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>& 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>& 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>& contact_nodes) #ifdef SUPPORT_TREE_DEBUG_TO_SVG coordf_t branch_radius_temp = 0; coordf_t max_y = std::numeric_limits::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>& 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>& 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>& 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>& 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>& contact_nodes) } Point next_layer_vertex = node.position; Point move_to_neighbor_center; + std::vector moves; + std::vector weights; const std::vector 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>& contact_nodes) #endif } +void TreeSupport::smooth_nodes(std::vector> &contact_nodes) +{ + std::map is_processed; + for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { + std::vector &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 &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 pts; + std::vector 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 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>& contact_nodes) { if (contact_nodes.empty()) @@ -2895,8 +3040,9 @@ void TreeSupport::adjust_layer_heights(std::vector>& 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& curr_layer_nodes = contact_nodes[layer_nr]; for (Node* node : curr_layer_nodes) { @@ -2995,8 +3141,13 @@ std::vector> TreeSupport::plan_layer_heights(std:: std::vector> layer_heights(contact_nodes.size(), std::pair(0.0, 0.0)); std::vector bounds; - if (layer_height == max_layer_height) - return std::vector>(); + 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 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::vectorget_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::vectoroverhang = &overhang_part; curr_nodes.emplace_back(contact_node); } @@ -3214,12 +3368,14 @@ void TreeSupport::generate_contact_points(std::vector -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::vectorposition(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::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; } diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 26abb74c5..1e7cc6398 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -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 merged_neighbours; + std::list 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> m_spanning_trees; std::vector< std::unordered_map> 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>& contact_nodes); + void drop_nodes(std::vector> &contact_nodes); + + void smooth_nodes(std::vector> &contact_nodes); void adjust_layer_heights(std::vector>& contact_nodes); @@ -432,6 +447,8 @@ private: void generate_toolpaths(); Polygons spanning_tree_to_polygon(const std::vector& spanning_trees, Polygons layer_contours, int layer_nr); Polygons contact_nodes_to_polygon(const std::vector& contact_nodes, Polygons layer_contours, int layer_nr, std::vector& radiis, std::vector& 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); }; } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a711b2eab..6659a7b69 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -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("support_type")) + && (config->opt_enum("support_style")==smsDefault || config->opt_enum("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{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); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index f4aed3f87..c79a4d5f3 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -78,7 +78,7 @@ std::map> 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}, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 22bebc27d..1f2a15979 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -540,10 +540,7 @@ int GLGizmoFdmSupports::get_selection_support_threshold_angle() SupportType support_type = obj_cfg.option("support_type") ? obj_cfg.opt_enum("support_type") : glb_cfg.opt_enum("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 "<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");