diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 330c53910a..009fd35a89 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -39,15 +39,6 @@ namespace Slic3r { #define unscale_(val) ((val) * SCALING_FACTOR) -inline unsigned int round_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer -{ - return (dividend + divisor / 2) / divisor; -} -inline unsigned int round_up_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer -{ - return (dividend + divisor - 1) / divisor; -} - inline double dot_with_unscale(const Point a, const Point b) { return unscale_(a(0)) * unscale_(b(0)) + unscale_(a(1)) * unscale_(b(1)); @@ -206,8 +197,8 @@ static void draw_contours_and_nodes_to_svg const ExPolygons &overhangs, const ExPolygons &overhangs_after_offset, const ExPolygons &outlines_below, - const std::vector &layer_nodes, - const std::vector &lower_layer_nodes, + 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" } ) @@ -216,7 +207,7 @@ static void draw_contours_and_nodes_to_svg 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)); @@ -255,14 +246,14 @@ static void draw_contours_and_nodes_to_svg #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); } @@ -1269,50 +1260,9 @@ static void _make_loops(ExtrusionEntitiesPtr& loops_entities, ExPolygons &suppor expoly_list.erase(first_iter); } - // draw connected loops - if (/*wall_count > 1 && wall_count<5*/0) { - // TODO this method may drop some contours - wall_count = std::min(wall_count, loops.size()); - Polylines polylines; - polylines.push_back(Polyline()); - Polyline& polyline = polylines.back(); - Point end_pt; - Point end_dir; - for (int wall_idx = 0; wall_idx < wall_count; wall_idx++) { - Polygon &loop = loops[wall_idx]; - if (loop.size()<3) continue; - // break the closed loop if this is not the last loop, so the next loop can attach to the end_pt - //if (wall_idx != wall_count - 1 && loop.first_point() == loop.last_point()) - // loop.points.pop_back(); + extrusion_entities_append_loops(loops_entities, std::move(loops), role, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - if (wall_idx == 0) { - polyline.append(loop.points); - } else { - double d = loop.distance_to(end_pt); - if (d < scale_(2)) { // if current loop is close to the previous one - polyline.append(end_pt); - ExtrusionPath expath; - expath.polyline.append(loop.points); - ExtrusionLoop extru_loop(expath); - extru_loop.split_at(end_pt, false); - polyline.append(extru_loop.as_polyline()); - }else{// create a new polylie if they are far away - polylines.push_back(Polyline()); - polyline = polylines.back(); - polyline.append(loop.points); - } - } - end_pt = polyline.points.back(); - end_dir = end_pt - polyline.points[polyline.points.size() - 2]; - Point perpendicular_dir = turn90_ccw(end_dir); - end_pt = end_pt + normal(perpendicular_dir, flow.scaled_spacing()); - } - - extrusion_entities_append_paths(loops_entities, polylines, role, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - } else { - extrusion_entities_append_loops(loops_entities, std::move(loops), role, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - } - } +} static void make_perimeter_and_inner_brim(ExtrusionEntitiesPtr &dst, const ExPolygon &support_area, size_t wall_count, const Flow &flow, ExtrusionRole role) { @@ -1654,210 +1604,6 @@ void TreeSupport::generate_toolpaths() ); } -Polygons TreeSupport::spanning_tree_to_polygon(const std::vector& spanning_trees, Polygons layer_contours, int layer_nr) -{ - 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; -} - -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::generate() { if (m_support_params.support_style == smsOrganic) { @@ -1865,7 +1611,7 @@ void TreeSupport::generate() return; } - std::vector> contact_nodes(m_object->layers().size()); + std::vector> contact_nodes(m_object->layers().size()); profiler.stage_start(STAGE_total); @@ -1907,7 +1653,7 @@ if (!m_object->config().tree_support_adaptive_layer_height) for (auto& layer : contact_nodes) { - for (Node* p_node : layer) + for (SupportNode* p_node : layer) { delete p_node; } @@ -2061,7 +1807,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(); @@ -2123,7 +1869,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); @@ -2134,7 +1880,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) { @@ -2155,12 +1901,12 @@ void TreeSupport::draw_circles(const std::vector>& contact_no 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 @@ -2347,7 +2093,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); @@ -2439,7 +2185,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (print->canceled()) break; m_object->print()->set_status(66, (boost::format(_L("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); @@ -2547,7 +2293,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } } -void TreeSupport::drop_nodes(std::vector>& contact_nodes) +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. @@ -2573,7 +2319,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) 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, support_line_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, support_line_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); @@ -2594,7 +2340,7 @@ 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; + 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()); @@ -2613,7 +2359,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (layer_heights[layer_nr].height < EPSILON) continue; auto& layer_radius = all_layer_radius[layer_nr]; auto& layer_node_dist = all_layer_node_dist[layer_nr]; - for (Node *p_node : contact_nodes[layer_nr]) { + for (SupportNode *p_node : contact_nodes[layer_nr]) { layer_node_dist.emplace(p_node->dist_mm_to_top); } size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr; @@ -2655,7 +2401,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(_L("Support: propagate branches at layer %d")) % layer_nr).str()); @@ -2685,14 +2431,14 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) //Group together all nodes for each part. const ExPolygons& parts = 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 (node.distance_to_top < 0) { // gap nodes do not merge or move - Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node, + SupportNode* next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node, print_z_next, height_next); get_max_move_dist(next_node); next_node->is_merged = false; @@ -2747,10 +2493,10 @@ 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. } @@ -2767,11 +2513,11 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) { 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). @@ -2780,7 +2526,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (node.type == ePolygon) { // Remove all 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]; coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor)); 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); @@ -2812,8 +2558,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 @@ -2821,7 +2567,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(m_ts_data->get_avoidance(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 = new SupportNode(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); @@ -2839,7 +2585,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); @@ -2855,10 +2601,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } //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; @@ -2866,7 +2612,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (node.type == ePolygon) { // polygon node do not merge or move const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position); - Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, + SupportNode * next_node = new SupportNode(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->is_merged = false; @@ -2887,7 +2633,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) unsupported_branch_leaves.push_front({ layer_nr, p_node }); } else { - Node* pn = p_node; + SupportNode* pn = p_node; for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) pn->support_floor_layers_above = bottom_interface_layers - i + 1; // +1 so the parent node has support_floor_layers_above=2 to_delete.insert(p_node); @@ -2897,7 +2643,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) // if the link between parent and current is cut by contours, mark current as bottom contact node if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { - Node* pn = p_node->parent; + SupportNode* pn = p_node->parent; for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) pn->support_floor_layers_above = bottom_interface_layers - i + 1; to_delete.insert(p_node); @@ -2919,7 +2665,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; @@ -3010,7 +2756,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex); - Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, + SupportNode * next_node = new SupportNode(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); @@ -3038,11 +2784,11 @@ 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 @@ -3053,7 +2799,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) contact_nodes[i_layer].erase(to_erase); to_free_node_set.insert(i_node); - for (Node* neighbour : i_node->merged_neighbours) + for (SupportNode* neighbour : i_node->merged_neighbours) { unsupported_branch_leaves.push_front({ i_layer, neighbour }); } @@ -3064,32 +2810,32 @@ 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) + for (SupportNode *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; if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged 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) { if (!node->is_processed) { std::vector pts; - std::vector branch; - Node * p_node = node; + std::vector branch; + SupportNode * p_node = node; // add a fixed head if (node->child) { pts.push_back(p_node->child->position); @@ -3125,11 +2871,11 @@ 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; + 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) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (SupportNode* 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); @@ -3137,7 +2883,7 @@ void TreeSupport::smooth_nodes(std::vector> &contact_nodes) } for (size_t i = 0; i < tree_nodes.size(); i++) { TreeNode& tree_node = tree_nodes[i]; - Node* p_node = idx2ptr[i]; + SupportNode* p_node = idx2ptr[i]; if (p_node->child) tree_node.children.push_back(ptr2idx[p_node->child]); if(p_node->parent) @@ -3159,7 +2905,7 @@ void TreeSupport::smooth_nodes(std::vector> &contact_nodes) #endif } -void TreeSupport::adjust_layer_heights(std::vector>& contact_nodes) +void TreeSupport::adjust_layer_heights(std::vector>& contact_nodes) { if (contact_nodes.empty()) return; @@ -3170,8 +2916,8 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ // TODO can we merge layers in a way that guaranttees smoothness? if (!print_config.independent_support_layer_height || is_slim) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (Node* node : curr_layer_nodes) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (SupportNode* node : curr_layer_nodes) { node->print_z = m_object->get_layer(layer_nr)->print_z; node->height = m_object->get_layer(layer_nr)->height; } @@ -3190,14 +2936,14 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ if (layer_height == max_layer_height) return; extremes.push_back(0); - for (Node* node : contact_nodes[0]) { + for (SupportNode* node : contact_nodes[0]) { node->print_z = m_object->get_layer(0)->print_z; node->height = m_object->get_layer(0)->height; } for (int layer_nr = 1; 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->support_roof_layers_below >0 || node->support_floor_layers_above == bot_intf_layers) { extremes.push_back(layer_nr); break; @@ -3206,7 +2952,7 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ if (extremes.back() == layer_nr) { // contact layer use the same print_z and layer height with object layer - for (Node* node : curr_layer_nodes) { + for (SupportNode* node : curr_layer_nodes) { node->print_z = m_object->get_layer(layer_nr)->print_z; node->height = m_object->get_layer(layer_nr)->height; } @@ -3230,11 +2976,11 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ coordf_t print_z = extr1z + step; assert(step >= layer_height - EPSILON); for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; 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; if (std::abs(print_z - curr_layer_nodes[0]->print_z) < step / 2 + EPSILON) { - for (Node* node : curr_layer_nodes) { + for (SupportNode* node : curr_layer_nodes) { node->print_z = print_z; node->height = step; } @@ -3242,7 +2988,7 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ } else { // can't clear curr_layer_nodes, or the model will have empty layers - for (Node* node : curr_layer_nodes) { + for (SupportNode* node : curr_layer_nodes) { node->print_z = 0.0; node->height = 0.0; } @@ -3251,7 +2997,7 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ } } -std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) +std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) { const PrintObjectConfig& config = m_object->config(); const PrintConfig & print_config = m_object->print()->config(); @@ -3350,7 +3096,7 @@ std::vector TreeSupport::plan_layer_heights(std::vector>& contact_nodes) +void TreeSupport::generate_contact_points(std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); @@ -3423,7 +3169,7 @@ void TreeSupport::generate_contact_points(std::vectorm_layer_outlines_below[layer_nr], candidate))) { - 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, + SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->type = ePolygon; contact_node->overhang = &overhang_part; @@ -3452,8 +3198,8 @@ void TreeSupport::generate_contact_points(std::vectorget_collision(0, layer_nr), candidate)) { constexpr bool to_buildplate = true; - Node * contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, - Node::NO_PARENT, print_z, height, z_distance_top); + SupportNode * contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, + SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->overhang = &overhang_part; curr_nodes.emplace_back(contact_node); added = true; @@ -3475,7 +3221,7 @@ void TreeSupport::generate_contact_points(std::vectoroverhang = &overhang_part; curr_nodes.emplace_back(contact_node); @@ -3491,7 +3237,7 @@ void TreeSupport::generate_contact_points(std::vector().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, + SupportNode *contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->overhang = &overhang_part; contact_node->is_corner = true; @@ -3551,16 +3297,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); } diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index bd6bc7e38b..8a19acc485 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -41,6 +41,142 @@ struct TreeNode { } }; +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) + , support_floor_layers_above(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) + : distance_to_top(distance_to_top) + , position(position) + , obj_layer_nr(obj_layer_nr) + , support_roof_layers_below(support_roof_layers_below) + , support_floor_layers_above(0) + , to_buildplate(to_buildplate) + , parent(parent) + , print_z(print_z_) + , height(height_) + , dist_mm_to_top(dist_mm_to_top_) + { + if (parent) { + type = parent->type; + overhang = parent->overhang; + if (dist_mm_to_top==0) + dist_mm_to_top = parent->dist_mm_to_top + parent->height; + parent->child = this; + for (auto& neighbor : parent->merged_neighbours) + neighbor->child = this; + } + } + +#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 + + /*! + * \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_merged = false; // this node is generated by merging upper nodes + bool is_corner = false; + bool is_processed = false; + const ExPolygon *overhang = nullptr; // 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 support_floor_layers_above; + 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; + 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; + } +}; + /*! * \brief Lazily generates tree guidance volumes. * @@ -227,140 +363,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) - , support_floor_layers_above(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) - , support_floor_layers_above(0) - , to_buildplate(to_buildplate) - , parent(parent) - , print_z(print_z_) - , height(height_) - , dist_mm_to_top(dist_mm_to_top_) - { - if (parent) { - type = parent->type; - overhang = parent->overhang; - if (dist_mm_to_top==0) - dist_mm_to_top = parent->dist_mm_to_top + parent->height; - parent->child = this; - for (auto& neighbor : parent->merged_neighbours) - neighbor->child = this; - } - } - -#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_merged = false; // this node is generated by merging upper nodes - bool is_corner = false; - bool is_processed = false; - const ExPolygon *overhang = nullptr; // 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 support_floor_layers_above; - 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; - 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; - } - }; int avg_node_per_layer = 0; float nodes_angle = 0; @@ -418,7 +420,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. @@ -432,18 +434,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 adjust_layer_heights(std::vector>& contact_nodes); + void adjust_layer_heights(std::vector>& contact_nodes); /*! 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. * @@ -457,18 +459,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); + void generate_contact_points(std::vector>& contact_nodes); /*! * \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); coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);