From 39ae64fc53abec794d740e36baaa13fd6fb35849 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 6 Jan 2024 21:27:46 +0800 Subject: [PATCH] ENH: accurate top z distance for tree support 1. accurate top z distance for tree support Also fix a bug that bottom z and top z distances are misused. jira: STUDIO-3000, STUDIO-5990 github: #1827 2. Change interface pattern to interlaced rectilinear when using support material. 2. clean up tree support code Change-Id: Icc8ce1b6844c841a6fbd1d623df707fdc8ed0f7b (cherry picked from commit da7412a48dfb5767918ef125b9d0fb9718c03a61) --- bbl/i18n/zh_cn/BambuStudio_zh_CN.po | 4 +- src/libslic3r/ClipperUtils.cpp | 4 + src/libslic3r/ClipperUtils.hpp | 1 + src/libslic3r/Layer.hpp | 2 +- src/libslic3r/Support/SupportParameters.hpp | 5 +- src/libslic3r/Support/TreeSupport.cpp | 934 +++++++------------- src/libslic3r/Support/TreeSupport.hpp | 348 ++++---- src/libslic3r/Support/TreeSupport3D.cpp | 6 +- src/libslic3r/utils.cpp | 17 +- src/slic3r/GUI/Preferences.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 6 +- 11 files changed, 540 insertions(+), 789 deletions(-) diff --git a/bbl/i18n/zh_cn/BambuStudio_zh_CN.po b/bbl/i18n/zh_cn/BambuStudio_zh_CN.po index 245bcf9cb..6a6fa7a2b 100644 --- a/bbl/i18n/zh_cn/BambuStudio_zh_CN.po +++ b/bbl/i18n/zh_cn/BambuStudio_zh_CN.po @@ -5884,11 +5884,11 @@ msgstr "" msgid "" "When using support material for the support interface, We recommend the " "following settings:\n" -"0 top z distance, 0 interface spacing, concentric pattern and disable " +"0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable " "independent support layer height" msgstr "" "当使用支持界面的支持材料时,我们推荐以下设置:\n" -"0顶层z距离,0接触层间距,同心图案,并且禁用独立支撑层高" +"0顶层z距离,0接触层间距,交叠直线图案,并且禁用独立支撑层高" msgid "" "Layer height exceeds the limit in Printer Settings -> Extruder -> Layer " diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index a444d5e25..ea8bd0097 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -667,6 +667,10 @@ Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Pol { return diff(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } +Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons & subject, const Slic3r::ExPolygons & clip, ApplySafetyOffset do_safety_offset) +{ + return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); +} Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 24d8c8196..f35fd29ba 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -431,6 +431,7 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygon // To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. Slic3r::Polygons diff_clipped(const Slic3r::Polygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::ExPolygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index dcf75304e..f91a33b9a 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -311,7 +311,7 @@ protected: bool need_extra_wall = false; AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {} }; - enum OverhangType { Detected = 0, Enforced }; + enum OverhangType { Detected = 0, Enforced, SharpTail }; std::vector area_groups; std::map overhang_types; }; diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 590a23549..d2f6e6393 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -141,6 +141,7 @@ struct SupportParameters { 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; @@ -152,7 +153,7 @@ struct SupportParameters { float base_angle; float interface_angle; coordf_t interface_spacing; - coordf_t support_expansion; + coordf_t support_expansion=0; coordf_t interface_density; coordf_t support_spacing; coordf_t support_density; @@ -161,5 +162,7 @@ struct SupportParameters { InfillPattern interface_fill_pattern; InfillPattern contact_fill_pattern; bool with_sheath; + bool independent_layer_height = false; + const double thresh_big_overhang = Slic3r::sqr(scale_(10)); }; } // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 97d9bfbfa..e3d8a7a48 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -19,7 +19,7 @@ #include "BuildVolume.hpp" #include "ClipperUtils.hpp" -#include +#include #include #include @@ -80,7 +80,7 @@ enum TreeSupportStage { class TreeSupportProfiler { public: - uint32_t stage_durations[NUM_STAGES]; + uint32_t stage_durations[NUM_STAGES] = { 0 }; uint32_t stage_index = 0; boost::posix_time::ptime tic_time; boost::posix_time::ptime toc_time; @@ -110,14 +110,15 @@ public: } void tic() { tic_time = boost::posix_time::microsec_clock::local_time(); } - void toc() { toc_time = boost::posix_time::microsec_clock::local_time(); } - void stage_add(TreeSupportStage stage, bool do_toc = false) + uint32_t toc() { + toc_time = boost::posix_time::microsec_clock::local_time(); + return (toc_time - tic_time).total_milliseconds(); + } + void stage_add(TreeSupportStage stage) { if (stage > NUM_STAGES) return; - if(do_toc) - toc_time = boost::posix_time::microsec_clock::local_time(); - stage_durations[stage] += (toc_time - tic_time).total_milliseconds(); + stage_durations[stage] += toc(); } std::string report() @@ -172,42 +173,23 @@ Lines spanning_tree_to_lines(const std::vector& spanning_tr #ifdef SUPPORT_TREE_DEBUG_TO_SVG -static std::string get_svg_filename(const std::string& tag = "bbl_ts", const std::string& layer_nr_or_z="") -{ - static bool rand_init = false; - - if (!rand_init) { - srand(time(NULL)); - rand_init = true; - } - - int rand_num = rand() % 1000000; - std::string prefix = "C:/Users/arthur.tang/AppData/Roaming/BambuStudioInternal/SVG/"; - //makedir(prefix); - std::string suffix = ".svg"; - std::string name = prefix + tag; - if(!layer_nr_or_z.empty()) - name+= "_" + layer_nr_or_z; - return name + suffix; -} - static void draw_contours_and_nodes_to_svg ( - std::string layer_nr_or_z, + std::string fname, const ExPolygons &overhangs, const ExPolygons &overhangs_after_offset, const ExPolygons &outlines_below, - const std::vector &layer_nodes, - const std::vector &lower_layer_nodes, - std::string name_prefix, - std::vector legends = { "overhang","avoid","outlines" }, std::vector colors = { "blue","red","yellow" } + const std::vector& layer_nodes, + const std::vector& lower_layer_nodes, + std::vector legends = { "overhang","avoid","outlines" }, + std::vector colors = { "blue","red","yellow" } ) { BoundingBox bbox = get_extents(overhangs); bbox.merge(get_extents(overhangs_after_offset)); bbox.merge(get_extents(outlines_below)); Points layer_pts; - for (TreeSupport::Node* node : layer_nodes) { + for (SupportNode* node : layer_nodes) { layer_pts.push_back(node->position); } bbox.merge(get_extents(layer_pts)); @@ -216,7 +198,7 @@ static void draw_contours_and_nodes_to_svg bbox.max.y() = std::max(bbox.max.y(), (coord_t)scale_(10)); SVG svg; - svg.open(get_svg_filename(name_prefix, layer_nr_or_z), bbox); + svg.open(fname, bbox); if (!svg.is_opened()) return; // draw grid @@ -228,38 +210,32 @@ static void draw_contours_and_nodes_to_svg svg.draw_outline(outlines_below, colors[2]); // draw legend - if (!lower_layer_nodes.empty()) { - svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), format("nPoints: %1%->%2%",layer_nodes.size(), lower_layer_nodes.size()).c_str(), "green", 2); - } - else { - svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), ("nPoints: " + std::to_string(layer_nodes.size())).c_str(), "green", 2); - } + svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), format("nPoints: %1%->%2%",layer_nodes.size(), lower_layer_nodes.size()).c_str(), "green", 2); svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), legends[0].c_str(), colors[0].c_str(), 2); svg.draw_text(bbox.min + Point(scale_(0), scale_(4)), legends[1].c_str(), colors[1].c_str(), 2); svg.draw_text(bbox.min + Point(scale_(0), scale_(6)), legends[2].c_str(), colors[2].c_str(), 2); // draw layer nodes svg.draw(layer_pts, "green", coord_t(scale_(0.1))); -#if 0 + // lower layer points layer_pts.clear(); - for (TreeSupport::Node *node : lower_layer_nodes) { + for (SupportNode* node : lower_layer_nodes) { layer_pts.push_back(node->position); } svg.draw(layer_pts, "black", coord_t(scale_(0.1))); // higher layer points layer_pts.clear(); - for (TreeSupport::Node* node : layer_nodes) { + for (SupportNode* node : layer_nodes) { if(node->parent) layer_pts.push_back(node->parent->position); } svg.draw(layer_pts, "blue", coord_t(scale_(0.1))); -#endif } static void draw_layer_mst -(const std::string &layer_nr_or_z, +(const std::string &fname, const std::vector &spanning_trees, const ExPolygons& outline ) @@ -272,7 +248,7 @@ static void draw_layer_mst bbox.merge(bb); } - SVG svg(get_svg_filename("mstree",layer_nr_or_z).c_str(), bbox); + SVG svg(fname, bbox); if (!svg.is_opened()) return; svg.draw(lines, "blue", coord_t(scale_(0.05))); @@ -281,48 +257,6 @@ static void draw_layer_mst svg.draw(spanning_tree.vertices(), "black", coord_t(scale_(0.1))); } -static void draw_two_overhangs_to_svg(SupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2) -{ - if (overhangs1.empty() && overhangs2.empty()) - return; - BoundingBox bbox1 = get_extents(overhangs1); - BoundingBox bbox2 = get_extents(overhangs2); - bbox1.merge(bbox2); - - SVG svg(get_svg_filename("two_overhangs", std::to_string(ts_layer->print_z)), bbox1); - if (!svg.is_opened()) return; - - svg.draw(union_ex(overhangs1), "blue"); - svg.draw(union_ex(overhangs2), "red"); -} - -static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines) -{ - if (polylines.empty()) - return; - BoundingBox bbox = get_extents(polylines); - - SVG svg(get_svg_filename("lightnings", std::to_string(ts_layer->print_z)), bbox); - if (!svg.is_opened()) return; - - int id = 0; - for (Polyline& pline : polylines) - { - int i1, i2; - for (size_t i = 0; i < pline.size() - 1; i++) - { - i1 = i; - i2 = i + 1; - svg.draw(Line(pline.points[i1], pline.points[i2]), "blue"); - svg.draw(pline.points[i1], "red"); - id++; - svg.draw_text(pline.points[i1], std::to_string(id).c_str(), "black", 1); - } - svg.draw(pline.points[i2], "red"); - id++; - svg.draw_text(pline.points[i2], std::to_string(id).c_str(), "black", 1); - } -} #endif // Move point from inside polygon if distance>0, outside if distance<0. @@ -659,7 +593,7 @@ static Point bounding_box_middle(const BoundingBox &bbox) } TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_params) - : m_object(&object), m_slicing_params(slicing_params), m_object_config(&object.config()) + : m_object(&object), m_slicing_params(slicing_params), m_support_params(object), m_object_config(&object.config()) { m_print_config = &m_object->print()->config(); m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers; @@ -706,7 +640,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p m_support_params.independent_layer_height = false; // do not use independent layer height for 3D tree support #endif #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename("machine_boarder"), m_object->bounding_box()); + SVG svg(debug_out_path("machine_boarder.svg"), m_object->bounding_box()); if (svg.is_opened()) svg.draw(m_machine_border, "yellow"); #endif } @@ -902,8 +836,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!overhang.empty()) { has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(format("sharp_tail_orig_%.02f", layer->print_z)), m_object->bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "red"); + SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { *expoly }); #endif } } @@ -1019,8 +952,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!overhang.empty()) has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(format("sharp_tail_%.02f",layer->print_z)), m_object->bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "red"); + SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), overhang); #endif } @@ -1072,17 +1004,14 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) #ifdef SUPPORT_TREE_DEBUG_TO_SVG const Layer* layer1 = m_object->get_layer(cluster.min_layer); - BoundingBox bbox = cluster.merged_bbox; - bbox.merge(get_extents(layer1->lslices)); - SVG svg(get_svg_filename(format("overhangCluster_%s-%s_%s-%s_tail=%s_cantilever=%s_small=%s", + std::string fname = debug_out_path("overhangCluster_%d-%d_%.2f-%.2f_tail=%d_cantilever=%d_small=%d.svg", cluster.min_layer, cluster.max_layer, layer1->print_z, m_object->get_layer(cluster.max_layer)->print_z, - cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang)), bbox); - if (svg.is_opened()) { - svg.draw(layer1->lslices, "red"); - svg.draw(cluster.merged_poly, "blue"); - svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), "lslices", "red", 2); - svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), "overhang", "blue", 2); - } + cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang); + SVG::export_expolygons(fname, { + { layer1->lslices, {"min_layer_lslices","red",0.5} }, + { m_object->get_layer(cluster.max_layer)->lslices, {"max_layer_lslices","yellow",0.5} }, + { cluster.merged_poly,{"overhang", "blue", 0.5} }, + { cluster.is_cantilever? layer1->cantilevers: offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled), {cluster.is_cantilever ? "cantilever":"erode1","green",0.5}} }); #endif if (!cluster.is_small_overhang) @@ -1128,9 +1057,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); } - for (auto &area : ts_layer->overhang_areas) { - ts_layer->overhang_types.emplace(&area, SupportLayer::Detected); - } + int nDetected = ts_layer->overhang_areas.size(); // enforcers now follow same logic as normal support. See STUDIO-3692 if (layer_nr < enforcers.size() && lower_layer) { float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX, @@ -1142,9 +1069,14 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); append(ts_layer->overhang_areas, enforcer_polygons); - ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), SupportLayer::Enforced); } } + int nEnforced = ts_layer->overhang_areas.size(); + + // fill overhang_types + for (size_t i = 0; i < ts_layer->overhang_areas.size(); i++) + ts_layer->overhang_types.emplace(&ts_layer->overhang_areas[i], i < nDetected ? SupportLayer::Detected : + i < nEnforced ? SupportLayer::Enforced : SupportLayer::SharpTail); if (!ts_layer->overhang_areas.empty()) { has_overhangs = true; @@ -1158,13 +1090,11 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (layer->overhang_areas.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) continue; - SVG svg(get_svg_filename("overhang_areas", std::to_string(layer->print_z)), m_object->bounding_box()); - if (svg.is_opened()) { - svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow"); - svg.draw(layer->overhang_areas, "orange"); - if (blockers.size() > layer->id()) - svg.draw(blockers[layer->id()], "red"); - } + SVG::export_expolygons(debug_out_path("overhang_areas_%.2f.svg", layer->print_z), { + { m_object->get_layer(layer->id())->lslices, {"lslices","yellow",0.5} }, + { layer->overhang_areas, {"overhang","red",0.5} } + }); + if (enforcers.size() > layer->id()) { SVG svg(format("SVG/enforcer_%s.svg", layer->print_z), m_object->bounding_box()); if (svg.is_opened()) { @@ -1634,7 +1564,7 @@ void TreeSupport::generate_toolpaths() float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); #ifdef SUPPORT_TREE_DEBUG_TO_SVG - std::string name = get_svg_filename("trees_polyline", std::to_string(ts_layer->print_z)); + std::string name = debug_out_path("trees_polyline_%.2f.svg", ts_layer->print_z); BoundingBox bbox = get_extents(ts_layer->base_areas); SVG svg(name, bbox); if (svg.is_opened()) { @@ -1654,223 +1584,12 @@ void TreeSupport::generate_toolpaths() ); } -Polygons TreeSupport::spanning_tree_to_polygon(const std::vector& spanning_trees, Polygons layer_contours, int layer_nr) +void deleteDirectoryContents(const std::filesystem::path& dir) { - Polygons polys; - auto& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; - for (MinimumSpanningTree mst : spanning_trees) { - std::vector points = mst.vertices(); - if (points.size() == 0) - continue; - std::map visited; - for (int i=0;i to_ignore; - for (int i = 0; i < points.size(); i++) { - if (visited[points[i]] == true) - continue; - - Polygon poly; - bool has_next = true; - Point pt1 = points[i]; - poly.points.push_back(pt1); - visited[pt1] = true; - - while (has_next) { - const std::vector& neighbours = mst.adjacent_nodes(pt1); - if (neighbours.empty()) - { - break; - } - - double min_ccw = std::numeric_limits::max(); - Point pt_selected = neighbours[0]; - has_next = false; - for (Point pt2 : neighbours) { - if (to_ignore.find(Line(pt1, pt2)) == to_ignore.end()) { - auto iter = mst_line_x_layer_contour_cache.find({ pt1,pt2 }); - if (iter != mst_line_x_layer_contour_cache.end()) { - if (iter->second) - continue; - } - else { - Polylines pls; - pls.emplace_back(pt1, pt2); - Polylines pls_intersect = intersection_pl(pls, layer_contours); - mst_line_x_layer_contour_cache.insert({ {pt1, pt2}, !pls_intersect.empty() }); - mst_line_x_layer_contour_cache.insert({ {pt2, pt1}, !pls_intersect.empty() }); - if (!pls_intersect.empty()) - continue; - } - - if (poly.points.size() < 2 || visited[pt2]==false) - { - pt_selected = pt2; - has_next = true; - break; - } - double curr_ccw = pt2.ccw(pt1, poly.points.back()); - if (curr_ccw < min_ccw) - { - min_ccw = curr_ccw; - pt_selected = pt2; - has_next = true; - } - } - } - if (has_next) { - poly.points.push_back(pt_selected); - to_ignore.insert(Line(pt1, pt_selected)); - visited[pt_selected] = true; - pt1 = pt_selected; - } - } - polys.emplace_back(std::move(poly)); - } - } - return polys; + for (const auto& entry : std::filesystem::directory_iterator(dir)) + std::filesystem::remove_all(entry.path()); } -Polygons TreeSupport::contact_nodes_to_polygon(const std::vector& contact_nodes, Polygons layer_contours, int layer_nr, std::vector& radiis, std::vector& is_interface) -{ - Polygons polys; - std::vector spanning_trees; - std::vector radiis_mtree; - std::vector is_interface_mtree; - // generate minimum spanning trees - { - std::map visited; - for (int i = 0; i < contact_nodes.size(); i++) - visited.emplace(contact_nodes[i], false); - std::unordered_set to_ignore; - - // generate minimum spaning trees - for (int i = 0; i < contact_nodes.size(); i++) { - Node* node = contact_nodes[i]; - if (visited[node]) - continue; - - std::vector points_to_mstree; - double radius = 0; - Point pt1 = node->position; - points_to_mstree.push_back(pt1); - visited[node] = true; - radius += node->radius; - - for (int j = i + 1; j < contact_nodes.size(); j++) { - Node* node2 = contact_nodes[j]; - Point pt2 = node2->position; - // connect to this neighbor if: - // 1) both are interface or both are not - // 3) not readly added - // 4) won't cross perimeters: this is not right since we need to check all possible connections - if ((node->support_roof_layers_below > 0) == (node2->support_roof_layers_below > 0) - && to_ignore.find(Line(pt1, pt2)) == to_ignore.end()) - { - points_to_mstree.emplace_back(pt2); - visited[node2] = true; - radius += node2->radius; - } - } - - spanning_trees.emplace_back(points_to_mstree); - radiis_mtree.push_back(radius / points_to_mstree.size()); - is_interface_mtree.push_back(node->support_roof_layers_below > 0); - } - } - auto lines = spanning_tree_to_lines(spanning_trees); -#if 1 - // convert mtree to polygon - for (int k = 0; k < spanning_trees.size(); k++) { - auto& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; - MinimumSpanningTree mst = spanning_trees[k]; - std::vector points = mst.vertices(); - std::map visited; - for (int i = 0; i < points.size(); i++) - visited.emplace(points[i], false); - - std::unordered_set to_ignore; - for (int i = 0; i < points.size(); i++) { - if (visited[points[i]]) - continue; - - Polygon poly; - Point pt1 = points[i]; - poly.points.push_back(pt1); - visited[pt1] = true; - - bool has_next = true; - while (has_next) - { - const std::vector& neighbours = mst.adjacent_nodes(pt1); - double min_ccw = -std::numeric_limits::max(); - Point pt_selected; - has_next = false; - for (Point pt2 : neighbours) { - if (to_ignore.find(Line(pt1, pt2)) == to_ignore.end()) { - auto iter = mst_line_x_layer_contour_cache.find({ pt1,pt2 }); - if (iter != mst_line_x_layer_contour_cache.end()) { - if (iter->second) - continue; - } - else { - Polylines pls; - pls.emplace_back(pt1, pt2); - Polylines pls_intersect = intersection_pl(pls, layer_contours); - mst_line_x_layer_contour_cache.insert({ {pt1, pt2}, !pls_intersect.empty() }); - mst_line_x_layer_contour_cache.insert({ {pt2, pt1}, !pls_intersect.empty() }); - if (!pls_intersect.empty()) - continue; - } - if (poly.points.size() < 2) - { - pt_selected = pt2; - has_next = true; - break; - } - double curr_ccw = pt2.ccw(pt1, poly.points.rbegin()[1]); - if (curr_ccw > min_ccw) - { - has_next = true; - min_ccw = curr_ccw; - pt_selected = pt2; - } - } - } - if (!has_next) - break; - - poly.points.push_back(pt_selected); - to_ignore.insert(Line(pt1, pt_selected)); - visited[pt_selected] = true; - pt1 = pt_selected; - } - polys.emplace_back(std::move(poly)); - radiis.push_back(radiis_mtree[k]); - is_interface.push_back(is_interface_mtree[k]); - } - } -#else - polys = spanning_tree_to_polygon(spanning_trees, layer_contours, layer_nr, radiis); -#endif - return polys; -} - -void TreeSupport::clear_contact_nodes(std::vector>& contact_nodes) -{ - for (auto& layer : contact_nodes) - { - for (Node* p_node : layer) - { - delete p_node; - } - layer.clear(); - } - contact_nodes.clear(); -} - - void TreeSupport::generate() { bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value); @@ -1896,7 +1615,7 @@ void TreeSupport::generate() m_ts_data->is_slim = is_slim; // Generate contact points of tree support - std::vector> contact_nodes(m_object->layers().size()); + std::vector> contact_nodes(m_object->layers().size()); std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); @@ -1950,7 +1669,7 @@ void TreeSupport::generate() draw_circles(contact_nodes); profiler.stage_finish(STAGE_DRAW_CIRCLES); - clear_contact_nodes(contact_nodes); + profiler.stage_start(STAGE_GENERATE_TOOLPATHS); m_object->print()->set_status(69, _u8L("Support: generate toolpath")); @@ -2157,7 +1876,7 @@ Polygons TreeSupport::get_trim_support_regions( return polygons_trimming; } -void TreeSupport::draw_circles(const std::vector>& contact_nodes) +void TreeSupport::draw_circles(const std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); const Print* print = m_object->print(); @@ -2215,7 +1934,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (print->canceled()) break; - const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); assert(ts_layer != nullptr); @@ -2226,7 +1945,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no continue; } - Node* first_node = curr_layer_nodes.front(); + SupportNode* first_node = curr_layer_nodes.front(); ts_layer->print_z = first_node->print_z; ts_layer->height = first_node->height; if (ts_layer->height < EPSILON) { @@ -2245,21 +1964,35 @@ void TreeSupport::draw_circles(const std::vector>& contact_no bool has_polygon_node = false; bool has_circle_node = false; bool need_extra_wall = false; + ExPolygons collision_sharp_tails; + ExPolygons collision_base; + auto get_collision = [&](bool sharp_tail) -> ExPolygons& { + if (sharp_tail) { + if (collision_sharp_tails.empty()) + collision_sharp_tails = m_ts_data->get_collision(m_object_config->support_top_z_distance.value, layer_nr); + return collision_sharp_tails; + } + else { + if (collision_base.empty()) + collision_base = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + return collision_base; + } + }; BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); - for (const Node* p_node : contact_nodes[layer_nr]) + for (const SupportNode* p_node : contact_nodes[layer_nr]) { if (print->canceled()) break; - const Node& node = *p_node; + const SupportNode& node = *p_node; ExPolygons area; // Generate directly from overhang polygon if one of the following is true: // 1) node is a normal part of hybrid support // 2) node is virtual - if (node.type == ePolygon || node.distance_to_top<0) { + if (node.type == ePolygon || (node.distance_to_top<0 && !node.is_sharp_tail)) { if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) area.emplace_back(node.overhang); else { @@ -2270,7 +2003,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } else { Polygon circle(branch_circle); - double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius; + double scale = node.radius / branch_radius; double moveX = node.movement.x() / (scale * branch_radius_scaled); double moveY = node.movement.y() / (scale * branch_radius_scaled); //BOOST_LOG_TRIVIAL(debug) << format("scale,moveX,moveY: %.3f,%.3f,%.3f", scale, moveX, moveY); @@ -2300,7 +2033,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no area.emplace_back(ExPolygon(circle)); // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. - if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only) { + if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) { ExPolygons overhang_expanded; if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) overhang_expanded.emplace_back(node.overhang); @@ -2317,12 +2050,12 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); - else if (layer_nr > 0 && node.support_roof_layers_below == 1) + else if (layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false) { append(roof_1st_layer, area); max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top); } - else if (layer_nr > 0 && node.support_roof_layers_below > 0) + else if (layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail == false) { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); @@ -2330,6 +2063,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } else { + area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top<=0)); append(base_areas, area); max_layers_above_base = std::max(max_layers_above_base, node.dist_mm_to_top); } @@ -2344,8 +2078,9 @@ void TreeSupport::draw_circles(const std::vector>& contact_no roof_1st_layer = std::move(offset2_ex(roof_1st_layer, contact_dist_scaled, -contact_dist_scaled)); // avoid object - //ExPolygons avoid_region_interface = m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr); - Polygons avoid_region_interface = get_trim_support_regions(*m_object, ts_layer, m_slicing_params.gap_object_support, m_slicing_params.gap_support_object, m_ts_data->m_xy_distance); + // arthur: do not leave a gap for top interface if the top z distance is 0. See STUDIO-3991 + Polygons avoid_region_interface = get_trim_support_regions(*m_object, ts_layer, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, + m_slicing_params.gap_support_object == 0 ? 0 : m_ts_data->m_xy_distance); if (has_circle_node) { roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface); roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface); @@ -2361,8 +2096,8 @@ void TreeSupport::draw_circles(const std::vector>& contact_no roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border); // let supports touch objects when brim is on - auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); - base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); + //auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + //base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); //base_areas = diff_clipped(base_areas, avoid_region); ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); @@ -2453,7 +2188,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no std::vector overhangs; for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) { if (print->canceled()) break; - const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); assert(ts_layer != nullptr); @@ -2500,10 +2235,6 @@ void TreeSupport::draw_circles(const std::vector>& contact_no overhangs.emplace_back(to_polygons(overhang)); contours.emplace_back(to_polygons(base_areas_lower)); printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; - -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - draw_two_overhangs_to_svg(m_object->get_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back())); -#endif } @@ -2547,7 +2278,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (print->canceled()) break; m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str()); - const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); assert(ts_layer != nullptr); @@ -2642,7 +2373,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; ExPolygons roofs = roof_areas; append(roofs, roof_1st_layer); if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; - draw_contours_and_nodes_to_svg(format("%d_%.2f", layer_nr, ts_layer->print_z), base_areas, roofs, ts_layer->lslices, {}, {}, "circles", {"base", "roof", "lslices"}, {"blue","red","black"}); + draw_contours_and_nodes_to_svg(debug_out_path("circles_%d_%.2f.svg", layer_nr, ts_layer->print_z), base_areas, roofs, ts_layer->lslices, {}, {}, {"base", "roof", "lslices"}, {"blue","red","black"}); } #endif // SUPPORT_TREE_DEBUG_TO_SVG @@ -2655,7 +2386,9 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } } -void TreeSupport::drop_nodes(std::vector>& contact_nodes) +double SupportNode::diameter_angle_scale_factor; + +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. @@ -2673,12 +2406,13 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) 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; + SupportNode::diameter_angle_scale_factor = diameter_angle_scale_factor; 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) { + auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width](const SupportNode *node, int power = 1) { double move_dist = node->max_move_dist; if (node->max_move_dist == 0) { if (node->radius == 0) node->radius = calc_branch_radius(branch_radius, node->dist_mm_to_top, diameter_angle_scale_factor); @@ -2698,7 +2432,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) std::vector &layer_heights = m_ts_data->layer_heights; 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()); @@ -2715,7 +2448,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) coordf_t print_z_next = layer_heights[layer_nr_next].print_z; coordf_t height_next = layer_heights[layer_nr_next].height; - std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. + 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_support_layer(layer_nr); m_object->print()->set_status(60, (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); @@ -2736,7 +2469,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) Lines pls_intersect = intersection_ln(ln, layer_contours); mst_line_x_layer_contour_cache.insert({ {a, b}, !pls_intersect.empty() }); mst_line_x_layer_contour_cache.insert({ ln, !pls_intersect.empty() }); - profiler.stage_add(STAGE_intersection_ln, true); + profiler.stage_add(STAGE_intersection_ln); if (!pls_intersect.empty()) return true; } @@ -2745,10 +2478,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) //Group together all nodes for each part. const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr];// m_ts_data->get_avoidance(0, layer_nr); - std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. - for (Node* p_node : layer_contact_nodes) + std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. + for (SupportNode* p_node : layer_contact_nodes) { - const Node& node = *p_node; + const SupportNode& node = *p_node; 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. { @@ -2798,31 +2531,31 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) profiler.tic(); //std::vector& spanning_trees = m_spanning_trees[layer_nr]; std::vector spanning_trees; - for (const std::unordered_map& group : nodes_per_part) + for (const std::unordered_map& group : nodes_per_part) { std::vector points_to_buildplate; - for (const std::pair& entry : group) + for (const std::pair& entry : group) { points_to_buildplate.emplace_back(entry.first); //Just the position of the node. } spanning_trees.emplace_back(points_to_buildplate); } - profiler.stage_add(STAGE_MinimumSpanningTree,true); + profiler.stage_add(STAGE_MinimumSpanningTree); #ifdef SUPPORT_TREE_DEBUG_TO_SVG coordf_t branch_radius_temp = 0; coordf_t max_y = std::numeric_limits::min(); - draw_layer_mst(std::to_string(ts_layer->print_z), spanning_trees, m_object->get_layer(layer_nr)->lslices); + draw_layer_mst(debug_out_path("mtree_%.2f.svg", ts_layer->print_z), spanning_trees, m_object->get_layer(layer_nr)->lslices); #endif for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++) { const MinimumSpanningTree& mst = spanning_trees[group_index]; //In the first pass, merge all nodes that are close together. - std::unordered_set to_delete; - for (const std::pair& entry : nodes_per_part[group_index]) + std::unordered_set to_delete; + for (const std::pair& entry : nodes_per_part[group_index]) { - Node* p_node = entry.second; - Node& node = *p_node; + SupportNode* p_node = entry.second; + SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) { continue; //Delete this node (don't create a new node for it on the next layer). @@ -2831,9 +2564,9 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (node.type == ePolygon) { // Remove all circle neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { - Node * neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode * neighbour_node = nodes_per_part[group_index][neighbour]; if(neighbour_node->type==ePolygon) continue; - coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor)); + coord_t neighbour_radius = scale_(neighbour_node->radius); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); if (is_inside_ex(node.overhang, neighbour) && is_inside_ex(node.overhang, pt_north) && is_inside_ex(node.overhang, pt_south) @@ -2863,8 +2596,8 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) move_out_expolys(avoid_layer, next_position, radius_sample_resolution + EPSILON, max_move_between_samples); } - Node* neighbour = nodes_per_part[group_index][neighbours[0]]; - Node* node_; + SupportNode* neighbour = nodes_per_part[group_index][neighbours[0]]; + SupportNode* node_; if (p_node->parent && neighbour->parent) node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top && p_node->parent) ? p_node : neighbour; else @@ -2872,7 +2605,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) // Make sure the next pass doesn't drop down either of these (since that already happened). node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node); const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); - Node * next_node = new Node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, + SupportNode * next_node = m_ts_data->create_node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, print_z_next, height_next); next_node->movement = next_position - node.position; get_max_move_dist(next_node); @@ -2889,7 +2622,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) { 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]; + SupportNode* neighbour_node = nodes_per_part[group_index][neighbour]; if (neighbour_node->type == ePolygon) continue; node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top); @@ -2898,16 +2631,17 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); to_delete.insert(neighbour_node); + neighbour_node->valid = false; } } } } //In the second pass, move all middle nodes. - for (const std::pair& entry : nodes_per_part[group_index]) + for (const std::pair& entry : nodes_per_part[group_index]) { - Node* p_node = entry.second; - const Node& node = *p_node; + SupportNode* p_node = entry.second; + const SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) { continue; @@ -2928,7 +2662,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } } if (index_biggest >= 0) { - Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, + SupportNode* next_node = m_ts_data->create_node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; next_node->overhang = std::move(overhangs_next[index_biggest]); @@ -2951,6 +2685,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } else { to_delete.insert(p_node); + p_node->valid = false; } continue; } @@ -2958,6 +2693,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { to_delete.insert(p_node); + p_node->valid = false; continue; } } @@ -2976,7 +2712,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) Point sum_direction(0, 0); for (const Point &neighbour : neighbours) { // do not move to the neighbor to be deleted - Node *neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode *neighbour_node = nodes_per_part[group_index][neighbour]; if (to_delete.find(neighbour_node) != to_delete.end()) continue; Point direction = neighbour - node.position; @@ -3067,7 +2803,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_layer_vertex); - Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, + SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->movement = movement; get_max_move_dist(next_node); @@ -3077,12 +2813,13 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (contact_nodes[layer_nr].empty() == false) { - draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), get_collision(0,layer_nr_next), + draw_contours_and_nodes_to_svg(debug_out_path("contact_points_%.2f.svg", contact_nodes[layer_nr][0]->print_z), get_collision(0,layer_nr_next), get_avoidance(branch_radius_temp, layer_nr), m_ts_data->m_layer_outlines[layer_nr], - contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" }); + contact_nodes[layer_nr], contact_nodes[layer_nr_next], { "overhang","avoid","outline" }, { "blue","red","yellow" }); - BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z; + BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer->next " << layer_nr << "->" << layer_nr_next << ", print_z=" << ts_layer->print_z + << ", num points: " << contact_nodes[layer_nr].size() << "->" << contact_nodes[layer_nr_next].size(); 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; @@ -3094,25 +2831,30 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) for (;! unsupported_branch_leaves.empty(); unsupported_branch_leaves.pop_back()) { const auto& entry = unsupported_branch_leaves.back(); - Node* i_node = entry.second; + SupportNode* i_node = entry.second; for (; i_node != nullptr; i_node = i_node->parent) { size_t i_layer = i_node->obj_layer_nr; - std::vector::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node); + std::vector::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node); if (to_erase != contact_nodes[i_layer].end()) { // update the parent-child chain - for (Node* parent : i_node->parents) { + if (i_node->parent) { + i_node->parent->child = i_node->child; + for (SupportNode* parent : i_node->parents) { if (parent->child==i_node) parent->child = i_node->child; + } } if (i_node->child) { - i_node->child->parents= i_node->parents; + i_node->child->parent = i_node->parent; + i_node->child->parents.erase(std::find(i_node->child->parents.begin(), i_node->child->parents.end(), i_node)); + append(i_node->child->parents, i_node->parents); } contact_nodes[i_layer].erase(to_erase); - to_free_node_set.insert(i_node); + i_node->valid = false; - for (Node* neighbour : i_node->merged_neighbours) + for (SupportNode* neighbour : i_node->merged_neighbours) { unsupported_branch_leaves.push_front({ i_layer, neighbour }); } @@ -3123,33 +2865,28 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) BOOST_LOG_TRIVIAL(debug) << "after m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size(); - for (Node *node : to_free_node_set) - { - delete node; - } - to_free_node_set.clear(); } -void TreeSupport::smooth_nodes(std::vector> &contact_nodes) +void TreeSupport::smooth_nodes(std::vector> &contact_nodes) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector &curr_layer_nodes = contact_nodes[layer_nr]; + std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; - for (Node *node : curr_layer_nodes) { + for (SupportNode *node : curr_layer_nodes) { node->is_processed = false; } } float max_move = scale_(m_object_config->support_line_width / 2); for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) { - std::vector &curr_layer_nodes = contact_nodes[layer_nr]; + std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; - for (Node *node : curr_layer_nodes) { + for (SupportNode *node : curr_layer_nodes) { if (!node->is_processed) { std::vector pts; std::vector radii; - std::vector branch; - Node * p_node = node; + std::vector branch; + SupportNode * p_node = node; // add a fixed head if it's not a polygon node, see STUDIO-4403 // Polygon node can't be added because the move distance might be huge, making the nodes in between jump and dangling if (node->child && node->child->type!=ePolygon) { @@ -3201,58 +2938,22 @@ void TreeSupport::smooth_nodes(std::vector> &contact_nodes) } } } - // save tree structure for viewing in python - auto& tree_nodes = m_ts_data->tree_nodes; - std::map ptr2idx; - std::map idx2ptr; - 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) { - ptr2idx.emplace(node, tree_nodes.size()); - idx2ptr.emplace(tree_nodes.size(), node); - tree_nodes.emplace_back(node->position, node->print_z); - } - } - for (size_t i = 0; i < tree_nodes.size(); i++) { - TreeNode& tree_node = tree_nodes[i]; - Node* p_node = idx2ptr[i]; - if (p_node->child) - tree_node.children.push_back(ptr2idx[p_node->child]); - if(p_node->parent) - tree_node.parents.push_back(ptr2idx[p_node->parent]); - } -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - nlohmann::json jj; - for (size_t i = 0; i < tree_nodes.size(); i++) { - nlohmann::json j; - j["pos"] = tree_nodes[i].pos; - j["children"] = tree_nodes[i].children; - j["linked"] = !(tree_nodes[i].pos.z() > 0.205 && tree_nodes[i].children.empty()); - jj.push_back(j); - } - - std::ofstream ofs("tree_nodes.json"); - ofs << jj.dump(); - ofs.close(); -#endif } #if USE_SUPPORT_3D -void TreeSupport::smooth_nodes(std::vector>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config) +void TreeSupport::smooth_nodes(std::vector>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config) { const coordf_t branch_radius = m_object_config->tree_support_branch_diameter.value / 2; const coordf_t branch_radius_scaled = scale_(branch_radius); const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.); // contact nodes to support elements TreeSupport3D::SupportElements move_bounds; - std::map node2elemIdx; - std::map elemIdx2node; - // save tree structure for viewing in python - auto& tree_nodes = m_ts_data->tree_nodes; + std::map node2elemIdx; + std::map elemIdx2node; int n = 0; 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) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (SupportNode* node : curr_layer_nodes) { if (node->distance_to_top < 0) continue; TreeSupport3D::SupportElementState state; state.type = node->type; @@ -3287,7 +2988,6 @@ void TreeSupport::smooth_nodes(std::vector>& contact_nodes, c move_bounds.emplace_back(state, std::move(circle)); node2elemIdx.emplace(node, n); elemIdx2node.emplace(n, node); - tree_nodes.emplace_back(node->position, node->print_z); n++; } } @@ -3300,7 +3000,7 @@ void TreeSupport::smooth_nodes(std::vector>& contact_nodes, c { for (size_t i = 0; i < move_bounds.size(); i++) { TreeSupport3D::SupportElement* elem = &move_bounds[i]; - Node* node= elemIdx2node[i]; + SupportNode* node= elemIdx2node[i]; size_t child = i; for(auto parent : node->parents) { size_t idx_parent = parent ? node2elemIdx[parent] : 0; @@ -3309,8 +3009,6 @@ void TreeSupport::smooth_nodes(std::vector>& contact_nodes, c TreeSupport3D::SupportElement* elem_parent = &move_bounds[idx_parent]; if (elem_parent->state.layer_idx == 0) child = -1; elements_with_link_down.push_back({ elem_parent, child }); - if(idx_parent>0) - tree_nodes[idx_parent].children.push_back(child); } } for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(contact_nodes.size()); ++layer_idx) { @@ -3363,7 +3061,7 @@ void TreeSupport::smooth_nodes(std::vector>& contact_nodes, c } #endif -std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) +std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) { const coordf_t max_layer_height = m_slicing_params.max_layer_height; const coordf_t layer_height = m_object_config->layer_height.value; @@ -3389,29 +3087,33 @@ std::vector TreeSupport::plan_layer_heights(std::vectorget_layer(0)->print_z, m_object->get_layer(0)->height, 0}; // Collect top contact layers + coordf_t print_z = layer_heights[0].print_z; for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { - if (!contact_nodes[layer_nr].empty()) - for (int i = 0; i < support_roof_layers + z_distance_top_layers + 1; i++) { - if (layer_nr - i > 0) { - bounds.push_back(layer_nr - i); - layer_heights[layer_nr - i].print_z = m_object->get_layer(layer_nr - i)->print_z; - layer_heights[layer_nr - i].height = m_object->get_layer(layer_nr - i)->height; + if (!contact_nodes[layer_nr].empty()) { + bounds.push_back(layer_nr); + layer_heights[layer_nr].print_z = contact_nodes[layer_nr].front()->print_z; + layer_heights[layer_nr].height = contact_nodes[layer_nr].front()->height; + if (layer_heights[layer_nr].bottom_z() - print_z < m_slicing_params.min_layer_height) { + layer_heights[layer_nr].height = layer_heights[layer_nr].print_z - print_z; + for (auto& node : contact_nodes[layer_nr]) { + node->height = layer_heights[layer_nr].height; } - else { - break; } + print_z = layer_heights[layer_nr].print_z; + BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[layer_nr].print_z << " " << layer_heights[layer_nr].height << " " + << layer_nr; } } std::set s(bounds.begin(), bounds.end()); bounds.assign(s.begin(), s.end()); - for (size_t idx_extreme = 0; idx_extreme < bounds.size(); idx_extreme++) { + for (size_t idx_extreme = 1; idx_extreme < bounds.size(); idx_extreme++) { int extr2_layer_nr = bounds[idx_extreme]; - coordf_t extr2z = m_object->get_layer(extr2_layer_nr)->bottom_z(); - int extr1_layer_nr = idx_extreme == 0 ? -1 : bounds[idx_extreme - 1]; - coordf_t extr1z = idx_extreme == 0 ? 0.f : m_object->get_layer(extr1_layer_nr)->print_z; + coordf_t extr2z = layer_heights[extr2_layer_nr].bottom_z(); + int extr1_layer_nr = bounds[idx_extreme - 1]; + coordf_t extr1z = layer_heights[extr1_layer_nr].print_z; coordf_t dist = extr2z - extr1z; // Insert intermediate layers. @@ -3423,7 +3125,8 @@ std::vector TreeSupport::plan_layer_heights(std::vector= layer_height - EPSILON); + //assert(step >= layer_height - EPSILON); + coordf_t extr2z_large_steps = extr2z; for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { // if (curr_layer_nodes.empty()) continue; if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) { @@ -3453,17 +3156,34 @@ std::vector TreeSupport::plan_layer_heights(std::vectornext_layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " - << i << "->" << layer_heights[i].next_layer_nr; + } + + for (i = 0; i < layer_heights.size(); i++) { + // there might be gap between layers due to non-integer interfaces + if (size_t next_layer_nr = layer_heights[i].next_layer_nr; + next_layer_nr>0 && layer_heights[i].height + EPSILON < layer_heights[i].print_z - layer_heights[next_layer_nr].print_z) + { + layer_heights[i].height = layer_heights[i].print_z - layer_heights[next_layer_nr].print_z; + } + // update interfaces' height + for (auto& node : contact_nodes[i]) { + node->height = layer_heights[i].height; + } + if (layer_heights[i].height > EPSILON) + BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights print_z, height, lower_layer_nr->layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " + << layer_heights[i].next_layer_nr << "->" << i; } return layer_heights; } -void TreeSupport::generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds) +void TreeSupport::generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds) { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); + const coordf_t max_bridge_length = scale_(config.max_bridge_length.value); + coord_t radius = scale_(config.tree_support_branch_diameter.value / 2); + radius = std::max(m_min_radius, radius); //First generate grid points to cover the entire area of the print. BoundingBox bounding_box = m_object->bounding_box(); @@ -3478,8 +3198,9 @@ void TreeSupport::generate_contact_points(std::vector grid_points; - for (auto x = -rotated_dims(0); x < rotated_dims(0); x += point_spread) { - for (auto y = -rotated_dims(1); y < rotated_dims(1); y += point_spread) { + coordf_t sample_step = std::max(point_spread, max_bridge_length / 2); + for (auto x = -rotated_dims(0); x < rotated_dims(0); x += sample_step) { + for (auto y = -rotated_dims(1); y < rotated_dims(1); y += sample_step) { Point pt(x, y); pt.rotate(cos_angle, sin_angle); pt += center; @@ -3491,12 +3212,18 @@ void TreeSupport::generate_contact_points(std::vectorthick_bridges) { z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height; + } } const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang. + // virtual layer with 0 height will be deleted + if (z_distance_top == 0) + z_distance_top = 0.001; size_t support_roof_layers = config.support_interface_top_layers.value; if (support_roof_layers > 0) @@ -3509,144 +3236,137 @@ void TreeSupport::generate_contact_points(std::vector all_nodes; - for (size_t layer_nr = 1; layer_nr < m_object->layers().size(); layer_nr++) - { - if (m_object->print()->canceled()) - break; - auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - const ExPolygons &overhang = ts_layer->overhang_areas; - auto & curr_nodes = contact_nodes[layer_nr]; - if (overhang.empty()) - continue; + tbb::concurrent_vector all_nodes; + tbb::parallel_for(tbb::blocked_range(1, m_object->layers().size()), [&](const tbb::blocked_range& range) { + for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + if (m_object->print()->canceled()) + break; + auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + Layer* layer = m_object->get_layer(layer_nr); + auto& curr_nodes = contact_nodes[layer_nr - 1]; + if (ts_layer->overhang_areas.empty()) continue; - std::unordered_set already_inserted; - auto print_z = m_object->get_layer(layer_nr)->print_z; - auto height = m_object->get_layer(layer_nr)->height; + std::unordered_set already_inserted; + auto print_z = m_object->get_layer(layer_nr)->print_z; + auto bottom_z = m_object->get_layer(layer_nr)->bottom_z(); + auto height = m_object->get_layer(layer_nr)->height; + bool added = false; // Did we add a point this way? + bool is_sharp_tail = false; - for (const ExPolygon &overhang_part : overhang) - { - BoundingBox overhang_bounds = get_extents(overhang_part); - if (support_style==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) { - if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr-1])) { - Point candidate = overhang_bounds.center(); - Node* contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z, - height, z_distance_top); - contact_node->type = ePolygon; + auto insert_point = [&](Point pt, const ExPolygon& overhang_part, bool force_add = false) { + Point hash_pos = pt / ((radius + 1) / 1); + SupportNode* contact_node = nullptr; + if (force_add || !already_inserted.count(hash_pos)) { + already_inserted.emplace(hash_pos); + bool to_buildplate = true; + // add a new node as a virtual node which acts as the invisible gap between support and object + // distance_to_top=-1: it's virtual + // print_z=object_layer->bottom_z: it directly contacts the bottom + // height=z_distance_top: it's height is exactly the gap distance + // dist_mm_to_top=0: it directly contacts the bottom + contact_node = m_ts_data->create_node(pt, -1, layer_nr - 1, support_roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, + unscale_(radius)); contact_node->overhang = overhang_part; - contact_node->radius = unscale_(overhang_bounds.radius()); + contact_node->is_sharp_tail = is_sharp_tail; + if (is_sharp_tail) { + int ind = overhang_part.contour.closest_point_index(pt); + auto n1 = (overhang_part.contour[ind] - overhang_part.contour[ind - 1]).cast().normalized(); + auto n2 = (overhang_part.contour[ind] - overhang_part.contour[ind + 1]).cast().normalized(); + contact_node->skin_direction = scaled((n1 + n2).normalized()); + } curr_nodes.emplace_back(contact_node); - continue; - } - } + added = true; + }; + return contact_node; + }; - // add supports at corners for both auto and manual overhangs, github #2008 - if (/*ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected*/1) { - // add points at corners - auto& points = overhang_part.contour.points; - int nSize = points.size(); - for (int i = 0; i < nSize; i++) { - auto pt = points[i]; - auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast().normalized(); - auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); - if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees - Node* contact_node = new Node(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z, - height, z_distance_top); - contact_node->overhang=overhang_part; - contact_node->is_corner = true; + for (const auto& area_type : ts_layer->overhang_types) { + const auto& overhang_part = *area_type.first; + is_sharp_tail = area_type.second == SupportLayer::SharpTail; + BoundingBox overhang_bounds = get_extents(overhang_part); + if (support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) { + if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr - 1])) { + Point candidate = overhang_bounds.center(); + SupportNode* contact_node = insert_point(candidate, overhang_part, true); + contact_node->type = ePolygon; + contact_node->radius = unscale_(overhang_bounds.radius()); curr_nodes.emplace_back(contact_node); - - Point hash_pos = pt / ((m_min_radius + 1) / 1); - already_inserted.emplace(hash_pos); - } - } - } - - auto& move_bounds_layer = move_bounds[layer_nr]; - if (move_bounds_layer.empty()) { - overhang_bounds.inflated(half_overhang_distance); - bool added = false; //Did we add a point this way? - for (Point candidate : grid_points) { - if (overhang_bounds.contains(candidate)) { - // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! - bool is_inside = is_inside_ex(overhang_part, candidate); - if (!is_inside) { - constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that - // fall between overhang areas on constant surfaces. - is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); - } - if (is_inside) { - // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing - Point hash_pos = candidate / ((m_min_radius + 1) / 1); - if (!already_inserted.count(hash_pos)) { - already_inserted.emplace(hash_pos); - constexpr bool to_buildplate = true; - Node* contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, 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; - } - } + continue; } } - if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. + // add supports at corners for both auto and manual overhangs, github #2008 { - auto bbox = overhang_part.contour.bounding_box(); - Points candidates; - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) - candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; - else - candidates = { bounding_box_middle(bbox) }; - - for (Point candidate : candidates) { - if (!overhang_part.contains(candidate)) - move_inside_expoly(overhang_part, candidate); - constexpr bool to_buildplate = true; - Node* contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, 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); - } - } - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim) { - // remove close points - for (auto it = curr_nodes.begin(); it != curr_nodes.end();) { - bool is_duplicate = false; - if (!(*it)->is_corner) { - Slic3r::Vec3f curr_pt((*it)->position(0), (*it)->position(1), scale_((*it)->print_z)); - for (auto& pt : all_nodes) { - auto dif = curr_pt - pt; - if (dif.norm() < point_spread / 2) { - delete (*it); - it = curr_nodes.erase(it); - is_duplicate = true; - break; - } + auto& points = overhang_part.contour.points; + int nSize = points.size(); + for (int i = 0; i < nSize; i++) { + auto pt = points[i]; + auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast().normalized(); + auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); + if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees + SupportNode* contact_node = insert_point(pt, overhang_part); + if (contact_node) { + contact_node->is_corner = true; } } - if (!is_duplicate) it++; + } + } + + auto& move_bounds_layer = move_bounds[layer_nr]; + if (move_bounds_layer.empty()) { + overhang_bounds.inflated(half_overhang_distance); + bool added = false; //Did we add a point this way? + for (Point candidate : grid_points) { + if (overhang_bounds.contains(candidate)) { + // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! + bool is_inside = is_inside_ex(overhang_part, candidate); + if (!is_inside) { + constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that + // fall between overhang areas on constant surfaces. + is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); + } + if (is_inside) { + SupportNode* contact_node = insert_point(candidate, overhang_part); + if (contact_node) + added = true; + } + } + } + + if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. + { + auto bbox = overhang_part.contour.bounding_box(); + Points candidates; + if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) + candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; + else + candidates = { bounding_box_middle(bbox) }; + + for (Point candidate : candidates) { + if (!overhang_part.contains(candidate)) + move_inside_expoly(overhang_part, candidate); + insert_point(candidate, overhang_part); + } + } + } + else { + for (auto& elem : move_bounds_layer) { + SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate, + SupportNode::NO_PARENT, print_z, height, z_distance_top); + contact_node->overhang = ExPolygon(elem.influence_area.front()); + curr_nodes.emplace_back(contact_node); + } } } - else { - for (auto& elem:move_bounds_layer) { - Node* contact_node = new Node(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate, - Node::NO_PARENT, print_z, height, z_distance_top); - contact_node->overhang = ExPolygon(elem.influence_area.front()); - curr_nodes.emplace_back(contact_node); - - } - } - } - if (!curr_nodes.empty()) nonempty_layers++; - for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } + 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(std::to_string(print_z), overhang, m_ts_data->m_layer_outlines_below[layer_nr], {}, - contact_nodes[layer_nr], {}, "init_contact_points", { "overhang","outlines","" }); + draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), ts_layer->overhang_areas, m_ts_data->m_layer_outlines_below[layer_nr], {}, + contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","outlines","" }); #endif - } + }} + ); // end tbb::parallel_for int nNodes = all_nodes.size(); avg_node_per_layer = nodes_angle = 0; if (nNodes > 0) { @@ -3669,16 +3389,16 @@ void TreeSupport::generate_contact_points(std::vector& nodes_layer, Node* p_node) +void TreeSupport::insert_dropped_node(std::vector& nodes_layer, SupportNode* p_node) { - std::vector::iterator conflicting_node_it = std::find(nodes_layer.begin(), nodes_layer.end(), p_node); + std::vector::iterator conflicting_node_it = std::find(nodes_layer.begin(), nodes_layer.end(), p_node); if (conflicting_node_it == nodes_layer.end()) //No conflict. { nodes_layer.emplace_back(p_node); return; } - Node* conflicting_node = *conflicting_node_it; + SupportNode* conflicting_node = *conflicting_node_it; conflicting_node->distance_to_top = std::max(conflicting_node->distance_to_top, p_node->distance_to_top); conflicting_node->support_roof_layers_below = std::max(conflicting_node->support_roof_layers_below, p_node->support_roof_layers_below); } @@ -3686,6 +3406,7 @@ void TreeSupport::insert_dropped_node(std::vector& nodes_layer, Node* p_n TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance, coordf_t max_move, coordf_t radius_sample_resolution) : m_xy_distance(xy_distance), m_max_move(max_move), m_radius_sample_resolution(radius_sample_resolution) { + clear_nodes(); for (std::size_t layer_nr = 0; layer_nr < object.layers().size(); ++layer_nr) { const Layer* layer = object.get_layer(layer_nr); @@ -3709,7 +3430,7 @@ const ExPolygons& TreeSupportData::get_collision(coordf_t radius, size_t layer_n RadiusLayerPair key{radius, layer_nr}; const auto it = m_collision_cache.find(key); const ExPolygons& collision = it != m_collision_cache.end() ? it->second : calculate_collision(key); - profiler.stage_add(STAGE_get_collision, true); + profiler.stage_add(STAGE_get_collision); return collision; } @@ -3721,7 +3442,7 @@ const ExPolygons& TreeSupportData::get_avoidance(coordf_t radius, size_t layer_n const auto it = m_avoidance_cache.find(key); const ExPolygons& avoidance = it != m_avoidance_cache.end() ? it->second : calculate_avoidance(key); - profiler.stage_add(STAGE_GET_AVOIDANCE, true); + profiler.stage_add(STAGE_GET_AVOIDANCE); return avoidance; } @@ -3745,6 +3466,37 @@ Polygons TreeSupportData::get_contours_with_holes(size_t layer_nr) const return contours; } +SupportNode* TreeSupportData::create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_, coordf_t radius_) +{ + // this function may be called from multiple threads, need to lock + m_mutex.lock(); + SupportNode* node = new SupportNode(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_); + contact_nodes.emplace_back(node); + m_mutex.unlock(); + return node; +} + +void TreeSupportData::clear_nodes() +{ + for (auto node : contact_nodes) { + delete node; + } + contact_nodes.clear(); +} + +void TreeSupportData::remove_invalid_nodes() +{ + for (auto it = contact_nodes.begin(); it != contact_nodes.end();) { + if ((*it)->valid==false) { + delete (*it); + it = contact_nodes.erase(it); + } + else { + it++; + } + } +} + coordf_t TreeSupportData::ceil_radius(coordf_t radius) const { size_t factor = (size_t)(radius / m_radius_sample_resolution); diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index e2e4ac12e..a32caca44 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -31,14 +31,155 @@ struct LayerHeightData size_t next_layer_nr = 0; LayerHeightData() = default; LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {} + coordf_t bottom_z() { + return print_z - height; + } }; -struct TreeNode { - Vec3f pos; - std::vector children; // index of children in the storing vector - std::vector parents; // index of parents in the storing vector - TreeNode(Point pt, float z) { - pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z }; +enum TreeNodeType { + eCircle, + eSquare, + ePolygon +}; + + +/*! + * \brief Represents the metadata of a node in the tree. + */ +struct SupportNode +{ + static constexpr SupportNode* NO_PARENT = nullptr; + + SupportNode() + : distance_to_top(0) + , position(Point(0, 0)) + , obj_layer_nr(0) + , support_roof_layers_below(0) + , to_buildplate(true) + , parent(nullptr) + , print_z(0.0) + , height(0.0) + {} + + // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height; + SupportNode(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, + coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0) + : distance_to_top(distance_to_top) + , position(position) + , obj_layer_nr(obj_layer_nr) + , support_roof_layers_below(support_roof_layers_below) + , to_buildplate(to_buildplate) + , parent(parent) + , print_z(print_z_) + , height(height_) + , dist_mm_to_top(dist_mm_to_top_) + , radius(radius_) + { + if (parent) { + parents.push_back(parent); + type = parent->type; + overhang = parent->overhang; + if (dist_mm_to_top == 0) + dist_mm_to_top = parent->dist_mm_to_top + parent->height; + if (radius == 0) + radius = parent->radius + (dist_mm_to_top - parent->dist_mm_to_top) * diameter_angle_scale_factor; + parent->child = this; + for (auto& neighbor : parent->merged_neighbours) { + neighbor->child = this; + parents.push_back(neighbor); + } + is_sharp_tail = parent->is_sharp_tail; + skin_direction = parent->skin_direction; + } + } + +#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node. + ~SupportNode() + { + parent = nullptr; + merged_neighbours.clear(); + } +#endif // DEBUG + + /*! + * \brief The number of layers to go to the top of this branch. + * 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 + + // all nodes will have same diameter_angle_scale_factor because it's defined by user + static double diameter_angle_scale_factor; + + /*! + * \brief The position of this node on the layer. + */ + Point position; + Point movement; // movement towards neighbor center or outline + mutable double radius = 0.0; + mutable double max_move_dist = 0.0; + TreeNodeType type = eCircle; + bool is_corner = false; + bool is_processed = false; + bool need_extra_wall = false; + bool is_sharp_tail = false; + bool valid = true; + ExPolygon overhang; // when type==ePolygon, set this value to get original overhang area + + /*! + * \brief The direction of the skin lines above the tip of the branch. + * + * This determines in which direction we should reduce the width of the + * branch. + */ + Point skin_direction; + + /*! + * \brief The number of support roof layers below this one. + * + * When a contact point is created, it is determined whether the mesh + * needs to be supported with support roof or not, since that is a + * per-mesh setting. This is stored in this variable in order to track + * how far we need to extend that support roof downwards. + */ + int support_roof_layers_below; + int obj_layer_nr; + + /*! + * \brief Whether to try to go towards the build plate. + * + * If the node is inside the collision areas, it has no choice but to go + * towards the model. If it is not inside the collision areas, it must + * go towards the build plate to prevent a scar on the surface. + */ + bool to_buildplate; + + /*! + * \brief The originating node for this one, one layer higher. + * + * In order to prune branches that can't have any support (because they + * can't be on the model and the path to the buildplate isn't clear), + * the entire branch needs to be known. + */ + SupportNode* parent; + std::vector parents; + SupportNode* child = nullptr; + + /*! + * \brief All neighbours (on the same layer) that where merged into this node. + * + * In order to prune branches that can't have any support (because they + * can't be on the model and the path to the buildplate isn't clear), + * the entire branch needs to be known. + */ + std::list merged_neighbours; + + coordf_t print_z; + coordf_t height; + + bool operator==(const SupportNode& other) const + { + return position == other.position; } }; @@ -62,6 +203,9 @@ public: * \param collision_resolution */ TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution); + ~TreeSupportData() { + clear_nodes(); + } TreeSupportData(TreeSupportData&&) = default; TreeSupportData& operator=(TreeSupportData&&) = default; @@ -100,9 +244,13 @@ public: Polygons get_contours(size_t layer_nr) const; Polygons get_contours_with_holes(size_t layer_nr) const; + SupportNode* create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, + coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0); + void clear_nodes(); + void remove_invalid_nodes(); std::vector layer_heights; - std::vector tree_nodes; + std::vector contact_nodes; private: /*! @@ -148,6 +296,7 @@ private: */ const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const; + tbb::spin_mutex m_mutex; public: bool is_slim = false; @@ -228,172 +377,6 @@ public: void detect_overhangs(bool detect_first_sharp_tail_only=false); - enum NodeType { - eCircle, - eSquare, - ePolygon - }; - - /*! - * \brief Represents the metadata of a node in the tree. - */ - struct Node - { - static constexpr Node* NO_PARENT = nullptr; - - Node() - : distance_to_top(0) - , position(Point(0, 0)) - , obj_layer_nr(0) - , support_roof_layers_below(0) - , to_buildplate(true) - , parent(nullptr) - , print_z(0.0) - , height(0.0) - {} - - // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height; - Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent, - coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0) - : distance_to_top(distance_to_top) - , position(position) - , obj_layer_nr(obj_layer_nr) - , support_roof_layers_below(support_roof_layers_below) - , to_buildplate(to_buildplate) - , parent(parent) - , print_z(print_z_) - , height(height_) - , dist_mm_to_top(dist_mm_to_top_) - { - if (parent) { - parents.push_back(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; - for (auto& neighbor : parent->merged_neighbours) { - neighbor->child = this; - parents.push_back(neighbor); - } - } - } - -#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node. - ~Node() - { - parent = nullptr; - merged_neighbours.clear(); - } -#endif // DEBUG - - /*! - * \brief The number of layers to go to the top of this branch. - * 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. - */ - Point position; - Point movement; // movement towards neighbor center or outline - mutable double radius = 0.0; - mutable double max_move_dist = 0.0; - NodeType type = eCircle; - bool is_corner = false; - bool is_processed = false; - bool need_extra_wall = false; - ExPolygon overhang; // when type==ePolygon, set this value to get original overhang area - - /*! - * \brief The direction of the skin lines above the tip of the branch. - * - * This determines in which direction we should reduce the width of the - * branch. - */ - bool skin_direction; - - /*! - * \brief The number of support roof layers below this one. - * - * When a contact point is created, it is determined whether the mesh - * needs to be supported with support roof or not, since that is a - * per-mesh setting. This is stored in this variable in order to track - * how far we need to extend that support roof downwards. - */ - int support_roof_layers_below; - int obj_layer_nr; - - /*! - * \brief Whether to try to go towards the build plate. - * - * If the node is inside the collision areas, it has no choice but to go - * towards the model. If it is not inside the collision areas, it must - * go towards the build plate to prevent a scar on the surface. - */ - bool to_buildplate; - - /*! - * \brief The originating node for this one, one layer higher. - * - * In order to prune branches that can't have any support (because they - * can't be on the model and the path to the buildplate isn't clear), - * the entire branch needs to be known. - */ - Node *parent; - std::vector parents; - Node *child = nullptr; - - /*! - * \brief All neighbours (on the same layer) that where merged into this node. - * - * In order to prune branches that can't have any support (because they - * can't be on the model and the path to the buildplate isn't clear), - * the entire branch needs to be known. - */ - std::list merged_neighbours; - - coordf_t print_z; - coordf_t height; - - bool operator==(const Node& other) const - { - return position == other.position; - } - }; - - struct SupportParams - { - Flow first_layer_flow; - 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; - - coordf_t support_layer_height_min; - // coordf_t support_layer_height_max; - - coordf_t gap_xy; - - float base_angle; - float interface_angle; - coordf_t interface_spacing; - coordf_t interface_density; - coordf_t support_spacing; - coordf_t support_density; - - InfillPattern base_fill_pattern; - InfillPattern interface_fill_pattern; - InfillPattern contact_fill_pattern; - bool with_sheath; - bool independent_layer_height = false; - const double thresh_big_overhang = SQ(scale_(10)); - }; - int avg_node_per_layer = 0; float nodes_angle = 0; bool has_overhangs = false; @@ -426,7 +409,7 @@ private: const PrintObjectConfig* m_object_config; SlicingParameters m_slicing_params; // Various precomputed support parameters to be shared with external functions. - SupportParams m_support_params; + SupportParameters m_support_params; size_t m_raft_layers = 0; size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; @@ -454,7 +437,7 @@ private: * save the resulting support polygons to. * \param contact_nodes The nodes to draw as support. */ - void draw_circles(const std::vector>& contact_nodes); + void draw_circles(const std::vector>& contact_nodes); /*! * \brief Drops down the nodes of the tree support towards the build plate. @@ -468,18 +451,18 @@ 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 smooth_nodes(std::vector> &contact_nodes); - void smooth_nodes(std::vector>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config); + void smooth_nodes(std::vector>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config); /*! BBS: MusangKing: maximum layer height * \brief Optimize the generation of tree support by pre-planning the layer_heights * */ - std::vector plan_layer_heights(std::vector> &contact_nodes); + std::vector plan_layer_heights(std::vector> &contact_nodes); /*! * \brief Creates points where support contacts the model. * @@ -493,19 +476,16 @@ private: * \return For each layer, a list of points where the tree should connect * with the model. */ - void generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds); + void generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds); /*! * \brief Add a node to the next layer. * * If a node is already at that position in the layer, the nodes are merged. */ - void insert_dropped_node(std::vector& nodes_layer, Node* node); + void insert_dropped_node(std::vector& nodes_layer, SupportNode* node); void create_tree_support_layers(); 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); - void clear_contact_nodes(std::vector>& contact_nodes); // get unscaled radius of node coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); // get unscaled radius(mm) of node based on the distance mm to top diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 434036e50..c62172cca 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -3543,7 +3543,7 @@ static std::pair extrude_branch( zmin = result.vertices.back().z(); float angle = angle_step; std::pair strip; - if (current.state.type == TreeSupport::NodeType::ePolygon) { + if (current.state.type == TreeNodeType::ePolygon) { strip = discretize_polygon(p1.cast(), current.influence_area, result.vertices); prev_strip = strip; strip = discretize_polygon(p2.cast(), current.influence_area, result.vertices); @@ -3571,7 +3571,7 @@ static std::pair extrude_branch( angle_step = M_PI / (2. * nsteps); auto angle = float(M_PI / 2.); std::pair strip; - if (current.state.type == TreeSupport::NodeType::ePolygon) { + if (current.state.type == TreeNodeType::ePolygon) { strip = discretize_polygon(p2.cast(), current.influence_area, result.vertices); } else { @@ -3597,7 +3597,7 @@ static std::pair extrude_branch( ncurrent = (v1 + v2).normalized(); float radius = unscaled(support_element_radius(config, current)); std::pair strip; - if (current.state.type == TreeSupport::NodeType::ePolygon) { + if (current.state.type == TreeNodeType::ePolygon) { strip = discretize_polygon(p2.cast(), current.influence_area, result.vertices); } else { diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index e3e2b8894..2fd34b643 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "Platform.hpp" #include "Time.hpp" @@ -282,9 +283,13 @@ static std::atomic debug_out_path_called(false); std::string debug_out_path(const char *name, ...) { - static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; + //static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; + auto svg_folder = boost::filesystem::path(g_data_dir) / "SVG/"; if (! debug_out_path_called.exchange(true)) { - std::string path = boost::filesystem::system_complete(SLIC3R_DEBUG_OUT_PATH_PREFIX).string(); + if (!boost::filesystem::exists(svg_folder)) { + boost::filesystem::create_directory(svg_folder); + } + std::string path = boost::filesystem::system_complete(svg_folder).string(); printf("Debugging output files will be written to %s\n", path.c_str()); } char buffer[2048]; @@ -292,7 +297,13 @@ std::string debug_out_path(const char *name, ...) va_start(args, name); std::vsprintf(buffer, name, args); va_end(args); - return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer); + + std::string buf(buffer); + if (size_t pos = buf.find_first_of('/'); pos != std::string::npos) { + std::string sub_dir = buf.substr(0, pos); + std::filesystem::create_directory(svg_folder.string() + sub_dir); + } + return svg_folder.string() + std::string(buffer); } namespace logging = boost::log; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index e9f7d6b4c..0438c7959 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1068,7 +1068,7 @@ wxWindow* PreferencesDialog::create_general_page() #endif auto title_user_experience = create_item_title(_L("User Experience"), page, _L("User Experience")); - auto item_priv_policy = create_item_checkbox(_L("Join Customer Experience Improvement Program."), page, _L(""), 50, "privacyuse"); + auto item_priv_policy = create_item_checkbox(_L("Join Customer Experience Improvement Program."), page, "", 50, "privacyuse"); wxHyperlinkCtrl* hyperlink = new wxHyperlinkCtrl(page, wxID_ANY, _L("What data would be collected?"), "https://bambulab.com/en/policies/privacy"); hyperlink->SetFont(Label::Head_13); item_priv_policy->Add(hyperlink, 0, wxALIGN_CENTER, 0); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4d79f738d..6abddece5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1489,9 +1489,9 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (opt_key == "support_interface_filament") { int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0 if (is_support_filament(interface_filament_id) && !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 && - m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipConcentric)) { + m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipRectilinearInterlaced)) { wxString msg_text = _L("When using support material for the support interface, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, concentric pattern and disable independent support layer height"); + "0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable independent support layer height"); msg_text += "\n\n" + _L("Change these settings automatically? \n" "Yes - Change these settings automatically\n" "No - Do not change these settings for me"); @@ -1500,7 +1500,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (dialog.ShowModal() == wxID_YES) { new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_spacing", new ConfigOptionFloat(0)); - new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipConcentric)); + new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipRectilinearInterlaced)); new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false)); m_config_manipulation.apply(m_config, &new_conf); }